If you are a .NET software developer, you have heard of F#. You may have read an article, seen a talk at a user group, or otherwise heard the buzz. However, if those means of reaching you have failed, at the very least, you have noticed it conspicuously appear in the list of languages you can base a solution on in Visual Studio 2010. If you write code on the .NET Framework, you would have to be living under a rock to have not heard of F#.
So you know it’s there - the question really is, what is it useful for? Why do you need another programming language? Is C# not good enough? Conventional wisdom has told us that C# is what you use for line of business apps, and that you only use F# for scientific apps, heavy math apps, or apps that do monte-carlo simulation. Strangely missing from that list are apps that you and I are likely to be asked to write. F#, according to some, is like the One Ring to Rule Them All - all powerful, but so dangerous that it must be kept under lock and key!
Balderdash, I say, to those who want to keep the power of F# locked up inside of research labs, universities and hedge funds. Power to the people! F# isn’t just for Wall Street; it is a compelling technology for Main Street as well. In this article, I will cover specific reasons, with examples, that demonstrate why you should learn this language and advocate for its adoption.
F# isn’t just for Wall Street; it is a compelling technology for Main Street as well.
The Evils of Null - Use F# to Set Yourself Free From NullReferenceException
If you have written any non-trivial amount of code, and actually seen such code through to production, you have likely had to deal with the dreaded NullReferenceException. It is one of the most common types of Exceptions, particularly in production. It is also one of the hardest to track down, particularly in complex systems with lots of moving parts.
The Source of NullReferenceException
A very common cause of NullReferenceException is failure to fulfill an implied contract. Much like a married man who gets in trouble for not getting the implied flowers that are due on an anniversary, systems get in trouble when they expect a value to exist in, say, a member variable, but none does, causing the offending NullReferenceException:
public class Invoice
{
public LineItems InvoiceLineItems { get; set; }
public void ProcessLineItems()
{
//what if LineItems is null?
foreach(var item in this.InvoiceLineItems)
{
//process each lineitem
}
}
}
In this example, Invoice contains an implied contract. Specifically, it states that InvoiceLineItems will be initialized before someone tries to call ProcessLineItems. Of course, one can fix this code rather easily:
public class Invoice
{
public LineItems InvoiceLineItems { get; set; }
public void ProcessLineItems()
{
if (this.InvoiceLineItems != null)
{
//Now we guarantee that LineItems isn’t null
foreach(var item in this.InvoiceLineItems)
{
Console.WriteLine( item );
}
}
}
}
Now at least we do not throw the NullReferenceException. However, we have also added “code-noise” making the code harder to understand at a glance, adding three lines that are there purely due to the fact that the object may be ill constructed at the time of the line items being processed.
Now lets look at this from the F# point of view:
type Invoice = { InvoiceLineItems : LineItems }
with
this.ProcessLineItems() =
this.InvoiceLineItems |> Seq.iter (
fun item -> Console.WriteLine( item )
)
Certainly, the syntax is different. Here, in F#, we use the type keyword to define an F# record - a concept with certain similarities to a C# class, but some really important differences. The properties of the record get defined in the braces that follow - the part that reads { InvoiceLineItems : LineItems } (note, I called the property InvoiceLineItems purely for clarity of type versus name when comparing the syntaxes - one could just call the property LineItems as well for brevity). The part after the with statement is where you can define methods for your record, like we do here with ProcessLineItems. The implementation of the method then passes the InvoiceLineItems to Seq.iter, which takes as its parameter the thing that should be done to each individual item when iterating.
That all said, do not let the syntax make you think F# is hard. It is certainly different, particularly coming from “curly-brace centric” languages like C#. Once you get used to it - usually after a couple weeks of working with the language - it will feel far more natural.
The more important part of this example is what, as an F# programmer, you do not need to do. Notice that in F#, I did not do the null check. That is because in F#, you can’t set the elements of a record to null:
//this won’t compile
let myInvoice = { InvoiceLineItems = null }
In F#, you are not allowed to build objects that are incomplete. If you are going to have an Invoice, you must have InvoiceLineItems - even if the set of InvoiceLineItems happens to be empty. Incomplete, ill-initialized objects are a large source of bugs in systems programmed in C# or other imperative languages. In F#, creating these sorts of bugs is far, far more difficult.
What about Optional Fields?
Of course, there are times when null is used to represent a lack of something. For example, let’s add something to our invoice:
public class Invoice
{
public LineItems InvoiceLineItems { get; set; }
public Address BillToAddress { get; set; }
public void ProcessLineItems()
{
if (this.InvoiceLineItems != null)
{
//Now we guarantee that LineItems isn’t null
foreach(var item in this.InvoiceLineItems)
{
Console.WriteLine( item );
}
}
}
}
In the case above, we have added an optional field - BillToAddress. Lack of a BillToAddress may indicate that the invoice is to be paid to the normal address. It is not uncommon to have fields like this that are optional - and it is common that programmers will use null as a sentinel value to represent the idea of “value not provided.” How does F# handle such a situation if null is not allowed?
type Invoice = { InvoiceLineItems : LineItems;
BillToAddress : Address option }
with
this.ProcessLineItems() =
this.InvoiceLineItems |> Seq.iter (
fun item -> Console.WriteLine( item )
)
If something is optional, in F#, you use the option keyword after the type name to indicate that you can have zero or one address. Great! But have we re-introduced null? Not so fast:
//with a bill to
let myInvoice = {
InvoiceLineItems = someLineItems;
BillToAddress = Some(myOtherAddress) }
//without a bill to
let myOtherInvoice = {
InvoiceLineItems = otherLineItems;
BillToAddress = None }
In the first example, where myInvoice is initialized, we set the address to Some(myOtherAddress). In F#, options take either a Some(T), where T is the underlying type of the object, or they take None, as the second example demonstrates.
None and Null - Same Thing?
An astute observer might recognize that None is mysteriously similar to null. However, the important distinction here is that None can only be used where the author of the type has specifically allowed it to be used. By contrast, in C#, null is the default value of all objects. This is the key - by severely limiting the scope over which absence can be represented, allowing it only where such absence is a valid state - you simplify your objects. You create fewer possible states of your object, you reduce the “surface area” you have to test and you make it far less likely that you will leave something as null on accident. This leads to vastly fewer NullReferenceExceptions, and ultimately, more stable software.
F# Could Have Saved the US Government 125 Million Dollars
In 1999, the world was excited as NASA was on the verge of a new round of exploration for the planet Mars. Only one problem - the probe, worth an estimated 125 million dollars, crashed, because a programmer mixed up metric and imperial units. Could F# have helped solve this problem?
A C#-Based Clothing Recommendation Engine
Perhaps so, perhaps not. F# will not will stop you from writing bad code that crashes orbiters. However, it does give you tools you can use to help avoid this class of problem should you choose to use these tools. Consider this example one might use in a piece of software that recommends what to wear given certain weather conditions:
public enum Clothing { Parka, Jacket, Shirt, Bikini }
public class ClothingRecommendationEngine
{
public static Clothing DoRecommendation(decimal t)
{
if (t < 0m) return Parka;
if (t < 15m) return Jacket;
if (t < 30m) return Shirt;
return Bikini; //unless your legs are hairy
}
}
This is rather straightforward - based on the temperature, recommend a given type of clothing. Upon closer inspection, you will notice that the algorithm assumes you are providing the temperature in Celsius. Imagine a programmer in, say, Belize or the Cayman Islands, two of three countries that have yet to convert to Celsius. You might end up doing this:
var recommendationForFreezingWeather =
ClothingRecommendationEngine.DoRecommendation(32m);
In the event that it’s freezing someday in Belize, your software may be perceived to have a bit of an issue!
Will F# Save Me From Freezing in Belize?
“Units of measure” is the concept in F# that solves this type of problem. We can rewrite the C# version using F# as follows:
type Clothing = Parka | Jacket | Shirt | Bikini
[<Measure>] type celsius
let clothingRecommendation (temp:decimal<celsius>) =
match temp with
| t when t < 0m<celsius> -> Parka
| t when t < 15m<celsius> -> Jacket
| t when t < 30m<celsius> -> Shirt
| _ -> Bikini
The code has many similar concepts, with a key difference being the [<Measure>] type Celsius line, and the associated parameter temp that takes on what, to a C# programmer, looks like a generic specification. In fact, you can read it the same way - just as you would have a List<T> in C#, in F#, you can have a decimal (or other numeric type) of M, so long as M is a unit of measure.
So how does this help? The consumer of the function will not be able to just pass any number to the recommendation engine:
//this will yield a compile failure...
let recommendationForFreezingWeatherWrong =
clothingRecommendation 32m
//as will this...
let recommendationForFreezingWeatherWrongAgain =
clothingRecommendation 32m<farenheight>
//but this will work!
let recommendationForFreezingWeatherRight =
clothingRecommendation 0m<celsius>
In doing this we have done two important things. The most obvious is that we have avoided a bug - the old code would have left you outside in freezing weather wearing a bikini. The F# code recommends a nice, warm coat instead. This alone makes it a win! But more importantly, the new version of the code is clearer. Before, the reader would have had to look all around the rest of the code to infer what units were expected. This new version makes the expected units much more explicit. The API of the F#-based recommendation engine requires much less guesswork.
This is no small thing. In any non-trivial piece of code, we deal with numeric concepts. Most interesting software deals in things that have numeric units - money, time, inventory or any other thing that we may count.
F# Will Save You from Mutation
Mutation - variables in your code may not be the root of all evil, but they are close. Granted, they are a necessary evil for a small subset of problems. But I am not too far off when I say that variables, as we know them in C#, are like little demons inside your codebase that make it buggier and harder to maintain.
Programming Without Variables? Are You Nuts?
The problem with variables is that they vary. Borrowing an analogy from mechanical engineering, they are the moving parts of your software. Any machine that does much needs moving parts. However, any mechanical engineer worth her salt does everything she can to reduce to the bare minimum the number of moving parts in a given machine. Doing so reduces the number of things that can go wrong, reducing the amount of maintenance a given machine needs.
Software is no different. You want as many moving parts (variables) as absolutely needed, but no more than that. Let’s look at a common example, a Money class:
public class Money
{
public string Currency { get; set; }
public decimal Amount { get; set; }
}
Despite this being a small class, this code has far too many moving parts. The first thing that needs to be done is that we need to make sure that when this object is created that both Currency and Amount have valid values upon creation:
public class Money
{
private Money() {} //remove parameterless cons
public Money(string currency, decimal amount)
{
Currency = currency;
Amount = amount;
}
public string Currency { get; set; }
public decimal Amount { get; set; }
}
This is a good first step. However, there are still too many moving parts. Changing currency should not be possible without changing the amount, since a currency change implies an amount change outside of a rare case of a 1:1 currency conversion ratio. We need to change the code to enforce that constraint:
public class Money
{
private readonly string _currency;
private Money() {}
public Money (string currency, decimal amount)
{
_currency = currency;
Amount = amount;
}
public string Currency { get { return _currency; } }
public decimal Amount { get; set; }
}
The improved version now has a _currency as a readonly property, which enforces a constraint that you can’t change the currency of an existing amount of money. It is one less moving part - one that should never move in the first place (i.e., if you want something as a different currency, create a new Money object after doing a conversion).
While the design is improved, frankly, I am still a bit paranoid. In a large system I could, in theory, pass my money to some function out there, it could store a reference to me, change my amount later, and without some explicit checks, I may not know it. To solve this problem, I can make the class immutable in C# as follows:
public class Money
{
private readonly string _currency;
private readonly decimal _amount;
private Money() {}
public Money (string currency, decimal amount)
{
_currency = currency;
_amount = amount;
}
public string Currency { get { return _currency; } }
public decimal Amount { get { return _amount; } }
}
With all that work done, I can proceed along my merry way. My class is nearly 100% immutable, outside of perhaps something changing my internals via reflection or changes to internals that occur during the execution of the constructor. Close enough for now - though it took a lot of coaxing.
F# - The Immutability Lover’s Best Friend
In C#, you can do immutability, but you will work for it. Every time you want to make something immutable, you have to go against a lot of established C# convention (no automatic properties for you!), and write lots of extra code. In F#, our Money class is as follows:
type Money = { Currency : string; Amount: decimal }
Yes, that is one line of code. If you are being paid by line of code, this may not make you particularly happy. However, if you care about productivity and making it easier to do the right thing, you probably should consider F#. The F# difference isn’t that it lets you write immutable code - you can do that in C# with a lot of work. F# makes it simpler to do so, which results in more code being right by default, which is a net win for your development team.
You Don’t Need to Understand Monads
Notice that in this article, not once did I mention the word monad. One of the biggest reasons that F# has a reputation as a “hard to learn language for people with too many IQ points” has nothing to do with the language itself, but the functional programming community. Many of these people, God bless them, work in environments where the word monad has as clear of a meaning that the word beer has to the rest of us. To these folks, catamorphism is a word to be used in normal parlance. Nothing wrong with that - these folks do a great job pushing the envelope and inventing things. We would not have things like Google, Pandora, or Siri without them!
However, just as we took things from NASA and the Space Program and integrated them into technologies that help us on a day-to-day basis, we need to capitalize on the best technologies that come from our research labs. F# is one of those things - once a product of Microsoft Research, it sits on your desk in Visual Studio 2010, ready for you to write less buggy, more stable software with it. The next move is yours - load it up in Visual Studio 2010, or even consider trying it out on the web at http://www.tryfsharp.org/. Either way, you will quickly be on your way to enjoying its many benefits.