devxlogo

F# 101

F# 101

riginally a research language from Microsoft Research, F# has long been a “secret weapon” in the arsenal of .NET programmers for doing statistically and mathematically heavy coding. More recently, however, a growing number of developers have begun to see the inherent advantages implicit in writing functional code, particularly in the areas of concurrency. The buzz has begun to approach interesting levels following on the heels of an announcement last year from the head of the Microsoft Developer Division, S. “Soma” Somasegar, that F# would be “productized” and fully supported by Microsoft as a whole, suddenly removing the stigma and licensing restrictions that surround research projects.

One of the more recent ideas that’s sweeping the industry is the notion of “domain-specific languages,” (DSLs) whereby a developer builds a language that others (typically either skilled knowledge workers/subject matter experts, or sometimes even other programmers) will use to solve the particular business problems facing them. As Neal Ford describes, two different kinds of DSLs permeate the space: internal DSLs, built on top of an existing language’s syntax, and external DSLs, built in some of the same ways as languages such as C# and Visual Basic?with a parser, an abstract syntax tree to store the parsed code, and either an interpreter to run the resulting program, or a code generator to turn it into an executable program.

As it turns out, F#’s derivation from a long line of functional languages means it has many of the tools necessary to make it easy to create simple external DSLs: discriminated unions, pattern matching, and combinatorial processing?all of which will probably be meaningless terms to the reader who’s never ventured outside of the object-oriented realm before. In this article, I’ll go over F#’s basic syntax, paying close attention to both those concepts that closely mirror that of the traditional O-O languages, and those that are brand new to the C#/VB programmer. While it’s entirely unreasonable to expect to master the F# language (or any non-trivial language) in a single column, by the end of this article, you should at least be able to recognize and read F# code, letting you dive more deeply into F# if and when appropriate.

Concepts

F# is a functional programming language for the CLR based on another functional/object language, OCaml.

F# is a CLR-adapted version of the Object-CAML language, also known as OCaml. As a functional language, F# espouses several core key ideas:

  • Static typing is a good safeguard to writing good code so long as the type information can stay out of the programmer’s way (also known as type inferencing).
  • Functions are values and have first-class semantics right alongside integers and strings; that shared and/or mutable state should be minimized (if not avoided outright).
  • Functional design permits and encourages composition and substitutability (which is where F# and other functional languages get much of their reputation for being “mathematical” languages).

Some of these concepts are more subtle than they may seem at first. For example, type inferencing means less explicit code needed to make the compiler happy, which in turn not only means that you can express the same concepts in less code in F#, but also that much of the verbosity of generics in C# effectively “fall away” when writing similar code in F#.

Before going too deeply into a more conceptual discussion, a quick glance at some F# code may help put a framework around some of these ideas.

Setup
To get started, you must install the F# binaries. As of this writing, F# still resides on the Microsoft Research site, the latest version is 1.9.4.17. You can find the download via the F# site. Look for the latest release link in the announcements column on the right-hand side (the actual download URL changes with each release).

When installed, F# not only puts several executables, assemblies, and assorted samples into the installation directory, but also registers its Visual Studio language service support. This registration gives the F# developer much the same development experience as the C# or VB developer, with an added bonus: an extra tool window called interactive mode, which allows the F# developer to type some F# code and execute it immediately, much as the Immediate Window works in the debugger. Simply “swipe” some code from the editor window with the mouse, hit Alt-Enter, and Visual Studio “automagically” pastes and executes the selected code in the Interactive Window. (This is by far the easiest way to explore the language and its concepts.)

Author’s Note: The F# package will also install into the Visual Studio Isolated Shell, so programmers who don’t have a copy of the commercial Visual Studio bits can still play along.

Functional programming languages encourage immutable state and zero “side effects,” making them ideal for programming in highly concurrent environments.

Assuming F# has installed successfully, fire up Visual Studio, create a new F# Project (under the “Other Project Types” node), add a new item (“F# Source file”), and call it file1.fs (the default). Notice that when you open the new file, the compiler populates it with a large number of F# example snippets, which serve as an easy way of getting started with the language. Have a look at the code in that template file later; for now, delete it all and replace it with the following code: F#’s version of the ubiquitous “Hello world” program:

   #light   let language = "F#"   printfn "Hello, %A" language

Congratulations! You are now an F# programmer.

Editor’s Note: This article was first published in the September/October 2008 issue of CoDe Magazine, and is reprinted here by permission.

“Let” There Be F#
The core of the F# language is, in many ways, the let expression. In the grand tradition of mathematics, where let was always used to establish the preconditions of a mathematical proof, F#’s let binds a value to an identifier, where the value can be either a traditional value, such as “12” or “Amanda” or some other constant, or a function taking zero or more inputs into the function for processing. Thus, in the following:

   let x = 5   let y = 12   let xtimesy x y = x * y

F# recognizes that x is bound to the value 5, y is bound to the value 12, and that xtimesy is bound to the function value x * y. Already, even in this simple code snippet, one of F#’s core concepts comes to the fore: Not only does F# realize that the values of “x” and “y” have to be integers (System.Int32, in fact) because of the fact that they are each bound to an integer constant, but also that the inputs to xtimesy must be integers, based on the fact that the definition is doing multiplication.

In the grand tradition of mathematics, where “let” was always used to establish the preconditions of a mathematical proof, F#’s “let” binds a value to an identifier, where the value can be either a traditional value, such as “12” or “Amanda” or some other constant, or a function taking zero or more inputs into the function for processing.

This type inference behavior isn’t perfect, by the way, because F# has inferred that the inputs should be integers, passing floating-point values in will cause F# to register it as an error:

   printfn "xtimesy 5 12 = %d" (xtimesy 5 12)   printfn "xtimesy 5.0 12.0 = %d" (xtimesy 5.0 12.0)

The first line will be OK, but the second will be flagged as an error by the Visual Studio environment, since the only definition of xtimesy currently requires integers. (Remember, F# is a statically typed language, despite what the terseness and lack of type information in the code may imply.) To fix this, you need a new definition of xtimesy that accepts floats, as well.

Actually, as it turns out, F# doesn’t permit overloading of functions at this level, though it is OK with doing it inside of types; to make the above code work, you have to write it as a function with a different name, such as xytimesyf:

   let xtimesy x y = x * y   let xtimesyf (x:double) (y:double) = x * y   printfn "xtimesy 5 12 = %d" (xtimesy 5 12)   printfn "xtimesyf 5.0 12.0 = %f"      (xtimesyf 5.0 12.0)

Although the preceding code is perfectly valid, the functional community rarely performs this kind of overloading in F#, thanks to other features of the language, such as automatic inference of generic arguments.

Note that the use of x and y in the function xtimesy is not bound against the x and y previously defined?like C#, F# has scope rules that say that names “inside” a new scope trump names from an outer scope. This applies, by the way, to nested functions just as it does to nested classes in C#.

In the second definition of xtimesy, the function definition chooses not to allow F# to infer the arguments (which it could do, since later in the program xtimesyf is used with floating-point values), but to explicitly declare the inputs to the function, each as a double. The parentheses are necessary here to help separate the first argument from the second; without them, an error occurs.

Normally, in F#, you can leave off the type designators, leaving F# to, in many cases, infer even more generic arguments about code than the original developer intended. For example, consider this function, which takes a tuple (pair) of arguments and reverses them:

   let swap (a, b) = (b, a)

F# interprets this function to take two arguments, of any particular type (they don’t even have to be the same type), essentially turning this into a generic function along the lines of this equivalent C# function:

   public static Tuple swap(T a, U b)   {       return new Tuple(b, a);   }

In fact, glancing at the implementation of the F# code in Reflector, this is precisely what’s displayed.

If this were the sum total of F#’s programming capabilities, the language wouldn’t merit much in the way of interest?it would still merit some, mind you, because these functions are exposed as static methods on a namespace defined to be the same as the file name (so, for example, the file1.exe assembly exports methods such as File1::xtimesy in the above example), and are thus usable from C#. But F# can also “do objects” as it turns out.

Classes in F#
Building a class in F# is actually fairly straightforward, with some interesting caveats and differences from traditional C# classes:

   type Person(firstName: string, lastName: string,      age: int) =   static member Nobody = Person("", "", 0)   member this.FirstName = firstName   member this.LastName = lastName   member this.Age = age   override this.ToString() : string =       System.String.Format("[Person {0} {1}         ({2} years old)]",         firstName, lastName, age)   let p = Person("Ted", "Neward", 37)   printfn "Creating new Person %A" p

The Person type here is certainly not complicated by any means, but several F# syntactic constructs make the class shorter and more succinct.

For example, consider the Person constructor. In C#, this would be a separate method whose name must match the type and have no return type, and the body of which is often taking parameters and storing them directly into fields of the same or similar name. This pattern is so common that in F# you define a primary constructor on the type definition line itself (the line type Person, above) and the implementation naturally defines fields of those names and types and generates the logic to store the constructor parameters into those fields.

Pattern matching provides a powerful linguistic construct only found in functional languages.

Or, as another case of F#’s succinctness, consider how type inferencing affects the definition of the FirstName, LastName, and Age properties: You don’t need to write type information there, because F# can infer it all from the definition around the fields (firstName, lastName, and age, respectively) used in the property definition.

Similarly, to create a new instance of the Person type, F# client code simply calls the Person constructor as a direct method call, rather than keying off of the new keyword as in C#. Again, this simple syntactic change reduces code verbosity?albeit only a single keyword?and helps make the language more readable.

Also, fairly obviously, F# uses no Begin/End or curly brace characters to delimit the start and end of the type declaration, nor does it use them in looping and other nesting constructs, such as the if expression discussed later. To avoid any such obvious delimiter, F# borrows a trick from Python, that of significant whitespace: in an F# program, the language determines whether a block of code is part of the nested block or the outer block based on its indentation level. Thus, in the definition of ToString above, the implementation of the method must be one indentation level “deeper” than the indentation level at which the other member definitions sit.

This aspect of F# is one of its more controversial features (just as it is with Python), but for the most part, it fades into the background during coding. However, one thing that may bite the budding F# programmer is that Visual Studio will complain if the editor is set to use tabs instead of spaces for indentation, so make sure to clear that option in the Tools ? Options dialog under the TextEditor ? F# node.

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.Nobody;           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)   else      (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.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist