Welcome back for another installment in my series on why I find
Microsoft F# to be an
exciting language for the .NET platform. If you're just joining us, below are
links to the articles in the series so far.
-
The Interactive Environment
-
Type-safe Format Strings
- Tuples
-
Breaking Up Tuples
- Result
Tuples
I have around 15-20 more articles planned, but I'm always looking for
suggestions. If you have a topic idea for the series, feel free to email
me at dustin AT diditwith.net.
One of the main reasons that I find F# to be so provocative is that it fully embraces
three programming paradigms: functional, imperative and object-oriented. Of
these, functional programming is the most favored, mostly due to its
OCaml heritage. Because of this, we can't move any further in this series
without introducing what functional programming is all about: functions!
In F#, a function declaration consists of the fun keyword, an argument, the
-> operator and finally, the function body.
fun arg -> body
In the F#
interactive environment, we can declare a function that takes an argument x
and returns its increment like so:
> fun x -> x + 1;;
val it : int -> int = <fun:clo@0>
If the -> operator looks strange to you, remember that it's
just a divider
that separates function arguments from function bodies.
The code above is somewhat equivalent to the following C# 3.0 lambda
expression:
x => x + 1;
Or this VB 9.0 lambda expression:
Function(x) x + 1
The biggest difference is that the F# function does not need to be assigned
to a .NET delegate
(or expression tree) as
the C# 3.0 and VB 9.0 lambda expressions do. This is an important point: F#
functions are not delegates. They're something else entirely.
Another point of interest is F#'s type inference. We didn't specify a type for x
in the function above, but F# determined that x is of type int and
that the function
returns an int.
F# worked this out from the literal 1 that appears in the function
body.
1 is an int. Therefore, x must be an int
because it is being added together with 1.
Finally, the function must return an int since its body
returns the
result of adding two ints, x and 1.
Because the literal that is added together with x is what triggers the type
inference, changing
the literal will change the type of the function. For example, changing 1 to 1.0
produces a function that increments
floats.
> fun x -> x + 1.0;;
val it : float -> float = <fun:clo@0>
This really isn't anything to write home about. After all, C# 3.0 and VB 9.0
handle type inference similarly for their respective lambda expressions. However, F#'s type inference
algorithm is extremely advanced. As this series progresses, you'll see functions
without any type annotations that the compiler will successfully type infer,
leaving you scratching your head.
At this point, we've successfully declared a function in F#. Unfortunately,
we can't use our function yet because it doesn't have a name. We've declared an
anonymous function. So, how do we give our function a
name? Well, let's back up a little bit to examine some syntax from a
previous article.
> let pair = 37, 5;;
val pair = int * int
The above example shows a variable, pair, being defined and assigned a value of
(37, 5).
The heart of this syntax is the keyword let.
Simply put, let binds a value to a name.
let name = value
In F#, functions are values. That's a small thing to say, but it has
enormous implications. Functions are treated in the same way
as any other value. That means that functions can be passed as
arguments to other functions, returned by other functions, contained within data
structures and bound by names as variables.
Because functions are values, we can give our function the name inc using let.
> let inc = (fun x -> x + 1);;
val inc : int -> int
And, we can call inc like so:
> inc 41;;
val it : int = 42
After learning to declare a function of one argument, the next logical step
is to declare a function of two arguments. This is actually done with
two functions. Consider the code below:
> let add = (fun x ->
- (fun y -> x + y));;
val add : int -> int -> int
That might look a bit confusing at first. If so, look again carefully. We're declaring a
function of one argument, x.
This function's body is another function of one argument, y.
The body of the inner function is x + y.
To call our function, we pass the first argument. That returns the inner
function to which we pass the second argument and finally receive the result of the
calculation. In essence, calling add
requires two function calls.
Normally, this is done all at once, as below:
> add 37 5;;
val it : int = 42
add is an example of a
curried function. The idea behind currying is simply transforming a function of multiple
arguments into a series of functions that each take one
argument. That's all it is. It's not hard. In fact, you can even torture .NET
delegates to
curry functions
in C#. Currying is a simple concept, but it's hard to grasp if you've never encountered
it before.
An interesting property of curried functions is the ability to partially
apply them. For example, if we pass 1 to our add
function above but don't pass the second argument, we are left with a function
of one argument that increments by 1. That is, we can define our inc function
in terms of add:
> let inc = add 1;;
val inc : (int -> int)
> inc 41;;
val it : int = 42
Cool!
The reality of our add definition above is that it is
far too verbose. It's easy to imagine
the nested functions quickly getting out of control when functions of more
arguments are declared. For this reason, F# provides a more concise way to declare
functions of multiple arguments.
> let add = (fun x y -> x + y);;
val add : int -> int -> int
> add 29 13;;
val it : int = 42
That's better. However, F# provides an even more concise syntax.
> let add x y = x + y;;
val add : int -> int -> int
> add 23 19;;
val it : int = 42
That's much better. Note that all three declarations of add are
equivalent. Each syntax produces a curried function. In F#, we get currying for free. If you
need to declare a function that isn't curried (e.g. to be called easily from C# or VB), you'd use a slightly
different syntax. But, that's another article.
I should also point out that F# managed to infer the type of add as int ->
int -> int even though there weren't any literals to trigger off of. In
future articles, we'll see F#'s type inference algorithm work "miracles"
like this over and over again. 
Next time, we'll be looking at
pattern matching and
how it fits into F#. See you then!