As you might have guessed functions are the building blocks of a functional language. It is very simple to define functions in F#, and for me this is one of the selling points, since defining a function requires much less syntactic sugar than in a lot of languages.
All the code of the samples can be downloaded here.
We’ll start with the simplest function I can think of, one that adds two numbers together and returns the result.
let add x y = x + y
The keyword let is used to tell the compiler we’re going to define something. It could have been just a value, but because the identifier add has two parameters after it, it’s a function. Everything after the equals sign is the function definition, and there's no need to explicitly specify a return explicitly value since the is returned automatically. If the programmer does not want a function to return a value they have to explicitly say they're not returning a value using the “()” notation. This is demonstrated below in the function pointless.
let pointless x = ()
The very observant may have noticed that you didn’t need brackets for the parameters of the add function. It is valid to specify the parameters with brackets but it has a slightly different meaning. If you specify the parameters with brackets this means the function takes a pair of values. If the parameters are specified without brackets this means the parameters can be passed individually, that is, it’s legal to pass in one parameter, and this returns a new function that requires one parameter. For example, our add function can be used to create a new function that only takes one parameter.
let addThree = add 3
In the example shown above the add function is used to create a new function addThree, which adds 3 to the given value.
Functional languages lack flow control in the classic sense of the term, because there is no flow to control. You can just all a function and it returns a value. You can still use flow control-like structures, but these too return values. For example, F# has an “if-then-else” construct but it is subtly different from an "if-then-else" construct in a imperative language. “If-then-else” in an imperative language means if the value is true execute one set of instructions otherwise execute another, in F# "if-then-else" means if the Boolean value is true return one value otherwise return another. For this reason "if-then-else" must always have an "else" clause since the construct must always return a value. To illustrate this, a function that uses the "if-then-else"construct to return a Boolean as a string is shown.
let boolToString x = if x then "True" else "False"
Because of this lack of flow control recursion is often used to specify how functions behave. Defining recursive functions is very simple in F#, simply use the rec keyword before the function name. A recursive definition of a function that takes an integer and returns a Fibonacci number is shown below.
let rec fib x =
match x with
| 0 -> 0
| 1 -> 1
| 2 -> 1
| x -> fib (x - 2) + fib (x - 1)
This function also illustrates F# pattern matching. The “match ... with” construct is used for pattern matching. In F#’s typically minimalist approach to syntax, you just specify the value you wish to match between the match and with key words, followed by a list of possible matching values along with the values they should return. The possible values and return values are separated by an arrow “->” and matching values are separated by a vertical bar “|”. An identifier can be given if you wish to match any value, or an underscore "_" can be used if you wish match any value but ignore it.
We have already seen that in F# functions can be returned from other functions, they can also be given as parameter values. This is illustrated below with function apply, which takes a function and a value and applies the function to the value.
let apply f x = f x
Obviously this function is not very useful but the idea of passing in a function as a value can be useful in many situations.
In F# it is not necessary to give functions names. This may sound like a strange idea, but a name is not always necessary. In fact, it is this idea from functional programming that is at least partially responsible for anonymous methods in C# 2.0. To do this you use the keyword fun with the identifier of a parameter and an arrow "->" to separate the function's definition. It is very useful to be able to create a simple function to pass into a method. To illustrate a function I have defined a simple function that adds 3 to a given value and passes it to the apply function defined earlier.
let _ = print_endline( "apply (fun x -> x + 3) 4 : " ^ any_to_string (apply (fun x -> x + 3) 4))
F# allows you to redefine operators such as plus "+". To do this use the let keyword and surround the operator in parentheses, this is illustrated below. This is a very powerful facility but is not necessarily advisable!
let (+) x y = x * y