RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


F# 101 : Page 3

F#, the latest member of the Visual Studio family of languages, offers some enticing advantages over C# and Visual Basic, stemming from its functional-object fusion nature.

Matchmaker, Matchmaker, Bind Me a Match
One powerful feature of a functional language such as F# is that of pattern-matching, in which a value can be "matched" against a series of potential targets, not unlike the way a switch/case works in C#. However, pattern-matching is much more powerful than switch/case, because not only can it match values against the determinant, it can also match against the type of the determinant and extract values out of the determinant and into local variables for further processing, all in one step.

Pattern-matching is much more powerful than switch/case, because not only can it match values against the determinant, but it can also match against the type of the determinant and extract values out of the determinant and into local variables for further processing, all in one step.
Here's an example. First, create an array of the Person type discussed above. The code will iterate over this collection and take various actions based on the contents:

   let people = 
       [| Person("Ted", "Neward", 37); 
          Person("Amanda", "Laucher", 27);
          Person("Matthew", "Podwysocki", 35); 
          Person("Rachel", "Appel", 35);
          Person("Scott", "Allen", 38);
          Person("Luke", "Hoban", 18); |]
The array syntax in F# is a bit different from C# or VB; the array contents are denoted using square brackets, and the elements are separated by semicolons.

Next, to iterate across the array (or any other collection in F#, which will more commonly be a list or sequence type), use a for comprehension construct:

   for per in people do
       printf "Matching %A: " per
On the surface, this looks very much like a traditional C# or VB for or For Each iteration construct, but internally the F# code works slightly differently. For now, the differences are immaterial.

Finally, the actual pattern match takes place, in which the current element, per, is matched against a series of potential candidates, and the first match executes the associated code block on the other side of the arrow in the following code:

   for per in people do
       printf "Matching %A: " per
   match per.FirstName with
       | "Ted" -> printfn "Hey, Ted!"
       | "Amanda" -> printfn "Hey, Amanda!"
       | x -> printfn "Hey, %s!" x
The pattern match begins by extracting the FirstName value out of the property on per, and matching that against the first candidate, which in this case is the constant value "Ted." If it finds it, it uses the F# library function printfn (which is essentially a wrapper around System.Console.WriteLine) to print a message to the console. Ditto for "Amanda," but in the third case, it takes whatever value is in per.FirstName and binds it to the local identifier x, and (in this case) prints it to the console.

Although it's not obvious from this example, a match construct, like most of F#'s syntax, returns a value. In this case, the value is unit, which is the functional equivalent of C#'s void (or VB's Nothing). This means that every branch of the match must return unit, which fortunately printfn does. There's no prohibition against returning a different value, by the way; it's just that this simple example only prints to the console, which returns unit.

You can also use pattern matching to match against more than one value at a time. To make this next example work, the F# language will require a little bit of help in the form of an active pattern construct, essentially a conversion function that can take a Person and break it up into constituent parts to simplify matching:

   let (|ParsePerson|) (p:Person) = 
   if p.Equals(Person.Nobody) then
      (null, null, 0)
      (p.FirstName, p.LastName, p.Age)
   for per in people do
      printf "Matching %A: " per
   match per with
      | ParsePerson (_, _, 0) ->
          printfn "You're Nobody"
      | ParsePerson ("Ted", _, _) -> 
          printfn "Hi, Ted!"
      | ParsePerson ("Amanda", _, _) -> 
          printfn "Hi, Amanda"
      | ParsePerson (fn, ln, a) when a > 30 -> 
          printfn "Hey, %s, ya old geezer!" fn
      | ParsePerson (fn, ln, a) when a < 30 -> 
          printfn "Hey, Kid %s, shaving yet?" ln
      | _ -> printfn "No idea who (or what) you are"
The preceding ParsePerson construct is the conversion function. It takes an instance of Person and returns a three-part tuple consisting of the Person's FirstName, LastName, and Age property values. Formally, F# gives this tuple a type of string * string * int, and that's what the pattern-match construct matches against.

In the first case, the "_" character matches against any possible value, so any Person with an Age of 0 will successfully match here, regardless of first or last name (which are thrown away). The second case matches when the first value in the tuple is equal to the string "Ted," and ditto for the third, "Amanda." The fourth, however, binds each of the three values in the tuple to individual identifiers (fn, ln, and a), but only when the a value is greater than 30. The fifth does the same, but this time only when the a value is less than 30. Finally, because there are still a few potential cases that could slip through the cracks (such as when a is exactly equal to 30—what then?), F# requires a wildcard match at the very end; think of this as the default clause in a traditional case/switch.

Overall, functional languages such as F#, Scala, and others, are encouraging a new kind of programming, known as language-oriented programming, that's slowly gathering a great deal of interest among programmers across all sorts of different platforms. Because of the functional nature of compilers and interpreters, not to mention the power of functional languages' abilities to recursively consume input, F# and its brethren are quickly becoming the languages of choice to build compilers, bytecode manipulators, interpreters, and other language-related tools.

At this point, you should be able to read a little F#. Obviously, there's much more to be said about the F# language; it is every bit as rich and powerful as the C# and Visual Basic languages. Fortunately, F# ships with a fairly extensive (if underdocumented) set of sample code for F# programmers willing to do a little spelunking in the F# installation directory. Better, F# resources are growing steadily, both in scope and availability. Currently, you can find the language's best language reference in the book Expert F# (Syme, Granicz, and Cisternio, APress, 2008), written by the language author himself (Don Syme). Other resources include HubFS, a developer portal dedicated to publishing information for F# programmers. And, of course, as F# nears its official release date, you can expect more and better MSDN documentation and improvements to the F# Web site.

In the meantime, take a few moments to explore F#, and see how thinking functionally can change the way you think about programming. If nothing else, it'll be a nice break from all those other dys-functional languages, like C#, Visual Basic, C++, HTML, Java….

Don't mind me, I'll just be going now.

Ted Neward is a Principal Consultant with ThoughtWorks, a global consulting firm focusing on .NET, Java and Ruby enterprise development. He is an internationally recognized speaker, instructor, consultant, and mentor and spends a great deal of time these days focusing on languages and execution engines such as the JVM and CLR.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date