F# is a functional programming language. Functional programming treats programs as mathematical expressions. It focuses on functions and constants that don't change, rather than variables and states. F# is a Microsoft programming language for concise and declarative syntax. Let's begin with a brief history of how this language came into existence. The first attempt at functional programming was Haskell .NET. F# development began in 2005 and after that, various versions came along. At the time of writing this chapter, F# 4.1 is the latest version; it was released in March 2017. This comes with Visual Studio 2017 and supports .NET Core.
The F# language can be used for the following tasks:
- To solve mathematical problems
- For graphic design
- For financial modeling
- For compiler programming
- For CPU design
It is also used for CRUD applications, web pages GUI games, and other programs.
In the F# language, we have two types of comments for a single line and for multiple lines. This is the same as C#. The following are the two types of comments:
- A single-line comment which starts with the
//
symbol.
Example: // returns an integer exit code
- A multi-line comment which starts with (
*
and ends with *
).
Example: (*Learn more about F# at http://fsharp.org *)
F# has a rich data type system. We can broadly classify them as:
- Integral types:
sbyte
, byte
, int16
, uint16
, int32
, uint32
, int64
, and bigint
- Floating point types:
float32
, float
, and decimal
- Text types:
char
and string
- Other types:
bool
These types are also referred to as fundamental primitive types in F#. Apart from these, F# has an exhaustive list of predefined types as well, such as lists, arrays, records, enumerations, tuples, units, sequences, and so on. It is recommended that a person learning F# goes through the official Microsoft documentation on F# at https://docs.microsoft.com/en-us/dotnet/fsharp/.
F# uses the let
keyword for the declaration of a variable, for example:
let square x = x*x
The compiler automatically detects this as a value type. If we pass a float value, the compiler will be able to understand that without declaring a data type. Variables in F# are immutable, so once a value is assigned to a variable, it can't be changed. They are compiled as static read-only properties.
The following example demonstrates this:
let x:int32 = 50
let y:int32 = 30
let z:int32 = x + y
Variables x
, y
, and z
are all of type int32
and are immutable, meaning their value cannot be changed.
Let's print their values. The syntax is as follows:
printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z
After the preceding code executes, the result is as follows:
x: 50
y: 30
z: 80
Now, suppose we want to modify the value of x
from 50
to 60
and check that z
reflects the updated sum; we will write the code as:
let x = 60
let y = 30
let z = x + y
On executing this code, we get the following errors, and rightly so because x
and z
are immutable:
Duplicate definition of value 'x'
Duplicate definition of value 'z'
The correct way of doing it would be to use mutable variables for the declaration, as shown here:
let mutable x = 60
let y = 30 //It's optional to make y mutable.
let mutable z = x + y
x <- 70
z <- x + y
On printing the values again, we will see:
x: 70
y: 30
z: 100
F# has the following operators:
- Arithmetic operators
- Comparison operators
- Boolean operators
- Bitwise operators
Let's discuss these operators in detail.
Arithmetic operators supported by the F# language are outlined in the following table. Assuming variable X = 10
and variable Y = 40
, we have the following expressions:
The following table shows all the comparison operators supported by F# . These operators return true
or false
.
Let's take X = 20
and Y = 30
:
The following table shows all the Boolean operators supported by the F# language. Let's take variable X
as true
and Y
as false
:
Bitwise operators work on bits and perform bit-by-bit operations. The truth tables for &&&
(bitwise AND), |||
(bitwise OR), and ^^^
(bitwise exclusive OR) are shown as follows. In the following table, the first variable is X
and the second variable is Y
:
It also supports ~~~
(Unary, effect of flipping bits) , <<<
(left shift operator), and >>>
(right shift operator).
Decision-making statements
The F# language has the following (if...else
and loop) types of decision-making statements.
The following table shows all the ways of implementing if
statements:
F# provides the following types of loop:
F# functions act like variables. We can declare and use them in the same way as we use variables in C#. A function definition starts with the let
keyword, followed by the function name and parameters, a colon, its type, and the right-side expression, showing what the function does. The syntax is follows:
Let functionName parameters [ : returnType] = functionbody
In the preceding syntax:
functionName
is an identifier of the function.parameters
gives the list of parameters separated by spaces. We can also specify an explicit type for each parameter and if not specified, the compiler tends to presume it from the function body as variables.functionbody
comprises an expression, or a compound expression, which has number of expressions. The final expression in the function body is the return value.returnType
is a colon followed by a type and it is optional. If the returnType
is not specified, then the compiler determines it from the final expression in the function body.
Have a look at the following example for our syntax:
let addValue (x : int) = 5 + x
A function can be called by passing the function name followed, by a space, and then arguments (if any) separated by spaces, as shown here:
let sum = addValue 3
We can perform many tasks using F# functions, some of which are as follows:
- We can create a new function and link that function with a type as it acts as a variable type:
let square x = x*x
- We can perform some calculations as well, such as:
let square x = x*x
- We can assign a value. Taking the same example:
let square x = x*x
- We can pass a function as a parameter to another function like this:
let squareValue = List.map square[1;2;3] // using square function
- We can return a function as a result of another function example:
let squareValue = List.map square[1;2;3]
The order of files in a project matters in an F# solution. The file used in any function should be placed above the file where the function is used, because F# has a forward-only model of compilation.
Unlike C#, where the file sequence doesn't matter, the sequencing of files does matter in F#. For example, consider that Program.fs
is using DotNetCorePrint.fs
. So, DotNetCorePrint.fs
should be placed above Program.fs
in the solution; otherwise, it will throw a compilation error. To move a file up or down, we can right-click on the file and select Move Up or the keys Alt + the up arrow to move the file. The ordering of the files in Solution Explorer
can be seen in the following screenshot:
Basic input/output syntax
We are now going to see how to write and read in F#. To read and write into the console, we can use the following commands:
- To write:
System.Console.Write("Welcome!")
- To read:
System.Console.Read()
- To print:
printfn "Hello"
Let's compare F# with C#: