I’ve decided to write a short tutorial that teaches F# to a reasonably intelligent C# programmer. The idea is to make it all meat and no fat so that no time is wasted.
I’m writing the samples using Visual Studio 2010 Beta 2. Microsoft has promised that you will be able to upgrade from Beta 2 to the final product without pain (unlike previous versions of Visual Studio where installing a beta could fuck up your computer royally) so I went ahead and installed it this morning.
So, open up VS and create a new F# Console project. Then type the following code:
open System let add x y = x + y let _ = let a = add 1 2 Console.WriteLine a ignore (Console.ReadLine ())
Let’s take a brief look at what we see:
First, notice that there are no semicolons. F#, by default, uses whitespace to determine the structure. Tabs are verboten in source files, and there are no indentation wars (two spaces or four) because how many spaces you need to indent depends on the keyword used in the previous line.
Next, at the top, we have open System. That’s the same thing as saying using System in C#, so that part is easy.
Following that we have the function add, which takes two parameters, x and y, and returns their sum. The function is weird because a) it’s declaration has no parenthesis, b) parameter types and the return type are not specified, and c) there is no return statement even though that’s what’s being returned. Let’s take a look at each of those:
a) No parenthesis. Get used to it, that’s the F# way. Parenthesis in F# are used for precedence operations – 1 * (2 + 3), and to quickly create pairs and tuples – (2, 3) is basically a value of type Pair<int,int>. Finally, F# uses empty parenthesis – () – to denote a parameter of type void. But no parenthesis for function declarations or calls.
b) No types specified. F# uses type inference to figure out the parameter type and the return value for each function. Notice that lower in the code we call add with two integers, 1 and 2, so the compiler will figure out that add takes two integers and based on x + y it will then figure out it also returns an integer.
c) No return statement. Think of F# functions as mathematical functions, e.g., f = x + y, with void being just another thing that a function can return.
After the function add, we have another function, with a weird name _. That’s the main function. Languages in the C family use a special function called main, F# uses _. Just a convention.
Let’s take a look at our main function. Since all the lines inside it are indented the same way, this is a simple sequence of statements that are executed one by one.
The first line:
let a = add 1 2
is a function call to add, with the parameters 1 and 2, with the result stored in a variable called a (it’s not really a variable but a “value”, but we’ll leave that for the next time). Again, no parenthesis in the function call. The reason why there are no parenthesis is that when you write just add, the compiler sees that and basically pushes a delegate to the function add on the stack. That delegate is Func<int,int,int>, familiar to those that use C# 3.0.
Then, after the token add, the compiler sees the value 1 and applies it to the previous value on the stack. So “add 1” basically combines Func<int,int,int> and an integer, and returns a new delegate, this time a Func<int,int>.
Finally, after add 1, which leaves a delegate int –> int on the stack, the compiler sees a 2. It combines the 2 with that delegate, and lo and behold, we have a real function call that is executed and add is called. The result is finally an integer which is assigned to the variable a.
So the basic function call in F# is simply push stuff onto the execution stack from left to right, and evaluate stuff at each step.
The next one is a call to Console.WriteLine with a as the parameter. Console.WriteLine is an Action<object> delegate, which combined with the object a that follows it results in a function call that prints out the value of a.
Finally, we want the program to pause so that you can see the result. So we write Console.ReadLine, but that just pushes the delegate Func<string> to the compiler’s stack. In order to actually call that function we have to pass it a parameter, which is weird since it doesn’t take any parameters. We get out of that bind by adding (), which is a value of type void, which then forces the function call to happen.
But that leaves with one unforeseen problem: Console.ReadLine returns a string, while our main function (_) must return void (“unit” in F# parlance). And remember, the compiler will puke, because everything is more or less a mathematical function, and it will tell you that your main function is trying to return a string instead of void. So in order to pacify it we call the function ignore with whatever Console.ReadLine returns, and the function ignore simply swallows the result and returns void.