It's now time to look at functions, the most basic and powerful building block in F#, and any other functional programming language for that matter. Functional programming languages use functions as first class constructs, in contrast to object-oriented programming, where objects and data are first class constructs. This means that in functional programming, functions will produce data based on the input and not based on state. In object-oriented programming, the state is encapsulated into objects and passed around. Functions are declared in the same way as variables were declared previously in the preceding snippets, with let
bindings. Have a look at the following code snippet:
If you try to evaluate the first
sum
function using Alt + Enter, F# Interactive will respond with a function like the following line of code:
This means that sum
is a function that takes two values of type int
and returns a value of type int
. The compiler simply knows that it's just the last type that is the return type.
Let's look at the following case where parameters of wrong types are passed to the function:
As seen in the modified version of the sum
function, the types are explicitly declared as float. This is a way of telling the compiler beforehand that float is the value to be used in the function. The first version of sum used type inference to calculate the types for x
and y
respectively and found it to be of type int
.
Learning about anonymous functions
Since it is common to create small helper functions in F# programming, F# also provides a special syntax for creating anonymous functions. These functions are sometimes called lambdas, or lambda functions. To define an anonymous function, the keyword fun
is used. Have a look at the following code snippet:
Explaining higher-order functions
Now the square function can be used by itself or as an argument to other functions or higher-order functions. Have a look at the following square function:
Here, the square function is passed as an argument to the function squareByFour
. The function squareByFour
is a higher-order function; it takes another function as an argument. Higher-order functions can take a function as an argument or return a function, or do both. This is an often used technique in functional programming to be able to construct new functions from existing functions and reuse them.
Though, currying is sometimes considered to be an advanced feature of programming languages, it makes the most sense on connection to functions and higher-order functions. The idea is not complicated at all, and once you have seen a couple of examples, the concept should be clear.
Let's look at the following sum
function:
Let's assume we want to reuse the function, but we may often call it for some fixed value of x
. That means we have a fixed x
, let's say 2
, and we vary the y
parameter. Have a look at the following:
Instead of having to write out the x
parameter every time, we can make use of the concept of currying. That means we create a new function with the first parameter fixed in this case. Take a look at the following function:
We have now saved ourselves from rewriting some arguments, but this is not the main reason. It's the ability to control the parameters and reuse functionality. More about currying will be covered in later chapters, but the basics were covered here in connection to higher-order functions.
Lists in F# are very useful, they are some of the most frequently used building blocks. They are the basic building blocks in functional languages and often replace the need of other types or classes. This is because the support for manipulating and creating lists and also being able to nest lists can be enough to replace custom types. You can think of lists as a sequence of values of the same type.
Lists in F# are the following:
A powerful way to store data
Immutable linked lists of values of any type
Often used as building blocks
One of the best ways to store data
This illustrates a list in F#, with a head and a tail, where each element is linked to the next.
Let's consider the following simple list of price information which are represented as floating points:
Suppose you want a list with values between 0 and 100, instead of writing them yourself, F# can do it for you. Take a look at the following lines of code:
This is fine if we just want a simple range with fixed size increments. Sometimes however, you may want to have a smaller increment, let's say 0.1, which is between 1.0 and 10.0. The following code shows how it is done:
Lists can be of any type, and type inference works here as well. Have a look at the following code:
However, if you mix the types in a list, the compiler will get confused about the actual type used:
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
Concatenating lists is useful when you want to add lists together. This is done using the @
operator. Have a look at the following code where the @
operator is used:
Let's have a look at some of the most commonly used functions in the List Module: Length
, Head
, Tail
, map
, and filter
respectively.
The function Length
will simply return the length of the list:
If you want the first element of a list, use Head
:
The rest of the list, meaning all other elements except the Head
, is defined as the Tail
:
You can also do some more interesting things with lists, such as calculating the square of all the elements one by one. Note that it's an entirely new list returned from the map
function, since lists are immutable. This is done using higher-order functions, where List.map
takes a lambda function defined to return the value of x*x
as seen in the following code:
Another interesting function is the filter
function of lists, which will return a new list matching the filter criteria:
Tuples are a group of unnamed but ordered values. The values can be of different types, if needed. You can think of them as more flexible versions of the Tuple class in C#.
Let's analyze the type information from the tuples in the REPL. The first tuple has the type information:
The *
symbol is used to separate the type elements for a tuple. It's simply a tuple of two floats. The next one is a bit more complex:
But the type inference figured it out without any doubts. The last one consists of expressions:
As you can see, the expressions are evaluated before the type data is analyzed. It may be useful to extract the values from a tuple, this can be done using simple patterns:
If you are not interested in the first value, use the wildcard character (the underscore) to simply ignore it. The wildcard is used throughout F#, for example, in pattern matching, which will be introduced in the next chapter.
The pipe operator is used a lot and it's defined as a function which takes the values on the left-hand side of the operator and applies them to the function on the right-hand side. There is another version of the pipe operator with various numbers of arguments, and more about them will be covered later.
The pipe-forward operator (|>) is the most common pipe operator:
This snippet first creates a list from 0 to 100, as illustrated in the section about lists previously. Then, the list is piped to the filter function with a conditional lambda function. Every even value in the list gets passed on to the next function. The map function will execute the lambda function to square every number. Finally, all numbers are summed, with the result of: