In this chapter, you will learn about the history of F# and its roots in other programming languages. We will also be introducing Visual Studio and the fundamental language constructs of F#. You will be comfortable using the interactive mode for prototyping the code step-by-step. You will get a better understanding of how to build programs in F# by putting pieces together. Also, the basics of the language are covered by using and evaluating the code in the Read Eval Print Loop (REPL).
In this chapter you will learn:
How to use F# with Visual Studio 2012
How to use F# Interactive to write the code in a new exploratory way
The basics of F# and how to write your first non-toy application
How functional programming will make you more productive
Before we dive in to the language itself, we should discuss why we need it in the first place. F# is a powerful language, which may sound like a cliché, but it combines multiple paradigms into real-life productivity and supports the .NET components and libraries natively as well as the Common Language Infrastructure (CLI). Functional programming has long been associated with academics and experts. F# is one of the few languages offering a complete environment that is mature enough to comfortably be integrated into an organization.
Also, F# has extensive support for parallel programming, where advanced features such as asynchronous and multi-threaded concepts are implemented as language constructs. It hides a lot of implementation details from the programmer. In F#, the functional programming paradigm is the main philosophy used to solve problems. The other paradigms, object-oriented and imperative programming, are prioritized to be used as subsidiaries and complements for this main paradigm. Reasons for them to coexist, involves compatibility and pragmatic, real-world productivity concerns.
We will start by introducing Visual Studio as the main tool of choice for this book. Although it's possible to use the standalone F# compiler and your favorite editor, you will most likely be more productive using Visual Studio 2012, as we will do throughout this book.
F# has been a part of Visual Studio since 2010. We will use the latest version of Visual Studio and F# throughout this book. This will enable us to use the latest functionality and enhancements available in Visual Studio 2012 and F# 3.0.
F# is open source, which means you can use it on any supported platform; it's not bound to Microsoft or Visual Studio. There is good support in other IDEs, such as MonoDevelop, which will run on Linux and Mac OS X.
Note
For more information about F# and the F# Software Foundation, visit http://fsharp.org.
Create a new project in Visual Studio for F#, which is to be used in this guide to explore the basics, as shown in the following sections.
Using the following steps, we can create a new project in Visual Studio:
To create your first F# project, open Visual Studio 12 and navigate to File | New | Project, then, from the menu select New Project.
Now you will see the New Project window appear. Select F# in the left panel and then select F# Application. You can name it anything you like. Finally, click on OK.
Now you have created your first F# application, which will just print the arguments passed to it.
Let's have a brief look at the program template generated by Visual Studio.

If you run this program, which will just print out the arguments passed to it, you will see a terminal window appear.

The [<EntryPoint>]
function in the preceding screenshot is the main function, which tells Visual Studio to use that particular function as the entry point for the program executable. We will not dig any deeper into this program template for now, but we will come back to this in the last three chapters when we'll build the trading system.
We will use an F# script file after having looked at the standard program template instead of exploring the basics of the language in a more interactive fashion. You can think of F# script files as notebooks, where you have executable code that you can explore in pieces in an incremental style:
Now that we have set up the basic project structure in Visual Studio, let's continue and explore F# Interactive.
F# Interactive is a way of executing parts of a program interactively. Doing this enables you as a programmer to explore parts of the code and how it behaves. You will have a more dynamic feel for writing code. It's also more fun. F# Interactive is a REPL for F#. This means, it will read the code, evaluate it, and then print out the result. It will then do this over and over again. It's much like a command line, where the code is executed and the result is displayed to the user.
To execute a code in F# Interactive, have a look at the following steps:
Select the source code you are interested in and press Alt + Enter.
You can write a simple line of code that will just print a string to the REPL's output window:
printfn "Hello World, from F"
It's also possible to right-click on the selected code and choose Execute In Interactive.
When executing the code using the Interactive mode, the result is shown in the F# Interactive Evaluation window below the code editor. It is also possible, and sometimes preferable to enter snippets into the Interactive window like the following example illustrates.
Enter the following line in the F# Interactive window and press Enter:
printfn "Hello World, from F#";;
This will be evaluated to the following in the REPL:
> printfn "Hello World, from F#";; Hello World, from F# val it : unit = ()
Using double semicolons (;;) after the line will terminate the input and enable you to just hit the Enter key, they are required if you type directly into the terminal window.
If you want to cancel the evaluation, it's possible to right-click on and then select Cancel Interactive Evaluation, or simply press Ctrl + Break.
We will now start our journey into functional programming using F#, and explore its capabilities in quantitative finance applications.
Let's start by looking at how values are declared, that is, how to bind values to names, mutability, and immutability.
To initialize and create a value, use the let
keyword. let
will bind the value on the right-hand side to the variable name on the left-hand side of the equals sign. This is a bind operator, a lot like math.
let sum = 4 + 5 let newsum = sum + 3
The let
binding is also used in the same way for binding functions to a name, as we will see in the following sections.
Once a variable is defined to have a particular value, it keeps that value indefinitely. There are exceptions to this, and shadowing can be used to override a previous assignment made within the same scope. Thus, variables in mathematics are immutable. Similarly, variables in F# are immutable with some exceptions.
Immutable variables are default in F#. They are useful because they are thread-safe and easier to reason about. This is one of the reasons you may have heard a lot about immutability recently. The concept is to solve the biggest issues and design flaws of concurrent programming, including shared mutable state. If the values do not change, there is no need to protect them either, which is one of the reasons for promoting immutability in concurrent programming.
If you try to alter the value of an immutable variable, you will encounter a message similar to the following:
let immutable = "I am immutable!" immutable <- "Try to change it..." … error FS0027: This value is not mutable
Sometimes, however, it's desirable to have variables that are mutable. Often the need arises in real-life applications when some global state is shared like a counter. Also, object-oriented programming and interoperability with other .NET languages makes the use of mutability unavoidable.
To create a mutable variable, you simply put the keyword mutable
in front of the name as shown in the following line of code:
let mutable name = firstname + lastname
To change the variable, after it is created, use the arrow operator (←
) as shown in the following line of code:
name ← "John Johnson"
This is a little bit different from other languages. But once you have wrapped your head around the concept, it makes more sense. In fact, it will most likely be one of the main ways to reason about variables in the future as well.
It may look like F# is a dynamic typed language like JavaScript, Ruby, or Python. In fact, F# is statically typed like C#, C++, and Java. It uses type inference to figure out the correct types. Type inference is a technique which is used to automatically deduce the type used by analyzing the code. This approach works remarkably well in nearly all situations. However, there are circumstances in which you as a programmer need to make clarifications to the compiler. This is done using type annotations, a concept we will look into in the following sections.
Let's explore some of the built-in types in F# using the REPL.
> let anInt = 124;; val anInt : int = 124
This means that F# figured out the type of anInt
to be of type int
. It simply inferred the type on the left-hand side to be of the same type as the right-hand side of the assignment. Logically, the type must be the same on both sides of the assignment operator, right?
We can extend our analysis into floating point numbers as shown in the following lines of code:
> let anFloat = 124.00;; val anFloat : float = 124.0
Because of the decimal sign, the type is determined to be of type float
. The same holds true for double
as shown in the following lines of code:
> let anDouble : double = 1.23e10;; val anDouble : double = 1.23e+10
For other types, it works in the same way as expected as shown in the following:
> let myString = "This is a string";; val myString : string = "This is a string"
All the primitive built-in types, except for unit
, have a corresponding type in .NET.
The following table shows the most common primitive types in F#:
Type |
.NET type |
Description |
---|---|---|
bool |
Boolean |
true or false |
byte |
Byte |
0 to 255 |
int |
Int32 |
-128 to 127 |
int64 |
Int64 |
-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
char |
Char |
0 to 18,446,744,073,709,551,615 |
string |
String |
Unicode text |
decimal |
Decimal |
Floating data type |
unit |
- |
Absence of an actual value |
void |
Void |
No type or value |
float |
Single |
64-bit floating point value |
double |
Double |
Same as above |
Note
For more information and all type you can visit http://msdn.microsoft.com/en-us/library/dd233210.aspx.
There are also other types that are built into the language which will be covered in more detail in the next chapter, such as lists, arrays, sequences, records, and discriminated unions.
Type inference means that the compiler will automatically deduce the type of an expression used in the code, based on the information provided from the programmer about the context of the expression. Type inference analyses the code, as you have seen in the preceding section, to determine types that are often obvious to the programmer. This spares the programmer from having to explicitly define the types of every single variable. It's not always needed to have the types defined to be able to understand the code, as seen in the preceding section for simple assignments of integers and floats. Type inference will make the code easier to write, and as a consequence, easier to read, leaving a lot of ceremony where it belongs.
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:
let sum (x,y) = x + y > sum (7, 7)
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:
val sum : x:int -> y:int -> int
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 sum (x:float, y:float) = x + y > sum(7.0, 7.0);; val it : float = 14.0
Let's look at the following case where parameters of wrong types are passed to the function:
> sum(7, 7);; ... error FS0001: This expression was expected to have type float but here has type int
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
.
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:
let square = (fun x → x * x) > square 2 val it : int = 4
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:
let squareByFour f f 4 > squareByFour square
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 sum x y = x + y
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:
sum 2 3 sum 2 4 sum 2 5
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:
let sumBy2 y = sum 2 y > sumBy2 3;; val it : int = 5 > sumBy2 4;; val it : int = 5 > sumBy2 5;; val it : int = 5
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:
let prices = [45.0; 45.1; 44.9; 46.0] > val prices : float list = [45.0; 45.1; 44.9; 46.0]
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:
let range = [0 .. 100] val range : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20;21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97; 98; 99; ...]
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:
let fineRange = [1.0 .. 0.1 .. 10.0] val fineRange : float list = [1.0; 1.1; 1.2; 1.3; 1.4; 1.5; 1.6; 1.7; 1.8; 1.9; 2.0; 2.1; 2.2; 2.3; 2.4; 2.5; 2.6; 2.7; 2.8; 2.9; 3.0; 3.1; 3.2; 3.3; 3.4; 3.5; 3.6; 3.7; 3.8; 3.9; 4.0; 4.1; 4.2; 4.3; 4.4; 4.5; 4.6; 4.7; 4.8; 4.9; 5.0; 5.1; 5.2; 5.3; 5.4; 5.5; 5.6; 5.7; 5.8; 5.9; 6.0; 6.1; 6.2; 6.3; 6.4; 6.5; 6.6; 6.7; 6.8; 6.9; 7.0; 7.1; 7.2; 7.3; 7.4; 7.5; 7.6; 7.7; 7.8; 7.9; 8.0; 8.1; 8.2; 8.3; 8.4; 8.5; 8.6; 8.7; 8.8; 8.9; 9.0; 9.1; 9.2; 9.3; 9.4; 9.5; 9.6; 9.7; 9.8; 9.9; 10.0]
Lists can be of any type, and type inference works here as well. Have a look at the following code:
> let myList = ["One"; "Two"; "Three"];; val myList : string list = ["One"; "Two"; "Three"]
However, if you mix the types in a list, the compiler will get confused about the actual type used:
let myList = ["One"; "Two"; 3.0];; ... This expression was expected to have type string but here has type float
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 myNewList = [1;2;3] @ [4;5;6];; val myNewList : int list = [1; 2; 3; 4; 5; 6] > myNewList;; val it : int list = [1; 2; 3; 4; 5; 6]
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:
> myNewList.Length;; val it : int = 6
If you want the first element of a list, use Head
:
> myNewList.Head;; val it : int = 1
The rest of the list, meaning all other elements except the Head
, is defined as the Tail
:
> myNewList.Tail;; val it : int list = [2; 3; 4; 5; 6]
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:
> List.map (fun x -> x * x) myNewList;; val it : int list = [1; 4; 9; 16; 25; 36]
Another interesting function is the filter
function of lists, which will return a new list matching the filter criteria:
> List.filter (fun x -> x < 4) myNewList;; val it : int list = [1; 2; 3]
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#.
// Tuple of two floats (1.0, 2.0) // Tuple of mixed representations of numbers (1, 2.0, 3, '4', "four") // Tuple of expressions (1.0 + 2.0, 3, 4 + 5)
Let's analyze the type information from the tuples in the REPL. The first tuple has the type information:
> (1.0, 2.0);; val it : float * float = (1.0, 2.0)
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:
> (1, 2.0, 3, '4', "four");; val it : int * float * int * char * string = (1, 2.0, 3, '4', "four")
But the type inference figured it out without any doubts. The last one consists of expressions:
> (1.0 + 2.0, 3, 4 + 5);; val it : float * int * int = (3.0, 3, 9)
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:
let (a, b) = (1.0, 2.0) printfn "%f %f" a b
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.
let (_, b) = (1.0, 2.0) printfn "only b %2.2f" b
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:
[0..100]|> List.filter (fun x -> x % 2 = 0)|> List.map (fun x -> x * 2)|> List.sum
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:
val it : int = 5100
Documenting your code is a good practice to get used to. Do you remember details about code you worked on some weeks ago? Then imagine yourself looking at the code you worked on several years ago. This is where documentation comes in. Just some hints about the logic will be sufficient for you and your colleges to grasp the main concepts behind the logic.
(* This is a comment on multiple lines *) /// Single line comment, supporting XML-tags // This is also a single line comment
The first application for doing something useful will be this Hello World of finance, which will illustrate some powerful yet simple concepts and strengths of F# and functional languages in general.
Let's start our journey into quantitative finance by looking at a simple yet illustrative example using Yahoo finance data. First, we will just put the data into the code to get used to the basic concepts.
First, we put some data in. In F# you can declare a list of strings on multiple lines like the following code:
/// Sample stock data, from Yahoo Finance let stockData = [ "2013-06-06,51.15,51.66,50.83,51.52,9848400,51.52"; "2013-06-05,52.57,52.68,50.91,51.36,14462900,51.36"; "2013-06-04,53.74,53.75,52.22,52.59,10614700,52.59"; "2013-06-03,53.86,53.89,52.40,53.41,13127900,53.41"; "2013-05-31,54.70,54.91,53.99,54.10,12809700,54.10"; "2013-05-30,55.01,55.69,54.96,55.10,8751200,55.10"; "2013-05-29,55.15,55.40,54.53,55.05,8693700,55.05" ]
We introduce a function for splitting strings on commas; this will create an array of strings. Don't forget to evaluate the parts of the program in F# Interactive using Alt + Enter. Doing this as a practice will make the number of errors less, and you will also be getting more comfortable and understand the types involved.
The type of the stockData
value is not explicitly declared, but if you evaluate it, you should see it is of type string list
:
val stockData : string list = ["2013-06-06,51.15,51.66,50.83,51.52,9848400,51.52"; ... "2013-05-29,55.15,55.40,54.53,55.05,8693700,55.05"] // Split row on commas let splitCommas (l:string) = l.Split(',') // Get the row with lowest trading volume let lowestVolume = stockData |> List.map splitCommas |> List.minBy (fun x -> (int x.[5]))
Evaluating the expression lowestVolume
will parse the strings in stockData
and extract the row with the lowest trading volume, column six. Hopefully, the result will be the row with date 2013-05-29, as in the following:
val lowestVolume : string [] = [|"2013-05-29"; "55.15"; "55.40"; "54.53"; "55.05"; "8693700";"55.05"|]
The following is the code listing for the program we developed in the previous section, the Hello World program of finance. Try it out for yourself and make changes to it if you like:
/// Open the System.IO namespace open System.IO /// Sample stock data, from Yahoo Finance let stockData = [ "2013-06-06,51.15,51.66,50.83,51.52,9848400,51.52"; "2013-06-05,52.57,52.68,50.91,51.36,14462900,51.36"; "2013-06-04,53.74,53.75,52.22,52.59,10614700,52.59"; "2013-06-03,53.86,53.89,52.40,53.41,13127900,53.41"; "2013-05-31,54.70,54.91,53.99,54.10,12809700,54.10"; "2013-05-30,55.01,55.69,54.96,55.10,8751200,55.10"; "2013-05-29,55.15,55.40,54.53,55.05,8693700,55.05" ] /// Split row on commas let splitCommas (l:string) = l.Split(',') /// Get the row with lowest trading volume let lowestVolume = stockData |> List.map splitCommas |> List.minBy (fun x -> (int x.[5]))
The pipe operator makes the logic of the program very straightforward. The program takes the list stockData
, splits for commas, then selects specific columns and applies a mathematical operator. Then, it selects the maximum value of these calculations and finally returns the first column of the row fulfilling the minBy
criteria. You can think of it as building blocks, where each piece is a standalone function on its own. Combining many functions into powerful programs is the philosophy behind functional programming.
Let's extend the preceding program to read data from a file instead. Since having it explicitly declared in the code is not that useful in the long run, as data tends to change. During this extension, we will also introduce exceptions and how they are used in .NET.
We start by writing a simple function to read all the contents from a file, where its path is passed as an argument. The argument is of type string, as you can see in the function header using the type annotation. Annotations are used either when the compiler can't figure out the type on its own, or when you as a programmer want to clarify the type used or enforce a certain type.
/// Read a file into a string array let openFile (name : string) = try let content = File.ReadAllLines(name) content |> Array.toList with | :? System.IO.FileNotFoundException as e -> printfn "Exception! %s " e.Message; ["empty"]
The function will catch FileNotFoundException
if the file is not found. There is also a new operator (:?
) before the exception type. This is a type test operator, which returns true if the value matches the specified type, otherwise, returns false.
Let's change the preceding code to use the content loaded from the file instead of the pre-coded stock prices.
/// Get the row with lowest trading volume, from file let lowestVolume = openFile filePath |> List.map splitCommas |> Seq.skip 1 |> Seq.minBy (fun x -> (int x.[5]))
There are some minor changes needed to the code to make it able to handle the input from the Comma-Separated Values (CSV) file. As with the input to the pipes, we use the result from the call to the openFile
function. Then, we split for commas as before. It was necessary to have a way to skip the first line; this is easy to do in F#, and you just insert a Seq.skip n
, where n is the number of elements in the sequence to skip.
printfn "Lowest volume, found in row: %A" lowestVolume
Here, we simply use printfn
formatted with %A
, which will just take anything and format for output (very convenient).
Let's look at one more example of this useful string formatter:
> printfn "This works for lists too: %A" [1..5];; This works for lists too: [1; 2; 3; 4; 5] val it : unit = ()
Let's look at the code for the entire program, which we looked at in the previous section.
/// Open the System.IO namespace open System.IO let filePath = @" table.csv" /// Split row on commas let splitCommas (l:string) = l.Split(',') /// Read a file into a string array let openFile (name : string) = try let content = File.ReadAllLines(name) content |> Array.toList with | :? System.IO.FileNotFoundException as e -> printfn "Exception! %s " e.Message; ["empty"] /// Get the row with lowest trading volume, from file let lowestVolume = openFile filePath |> List.map splitCommas |> Seq.skip 1 |> Seq.minBy (fun x -> (int x.[5])) /// Use printfn with generic formatter, %A printfn "Lowest volume, found in row: %A" lowestVolume
Using the interactive mode in Visual Studio and being able to write the program in smaller building blocks using prototyping is a great way of writing software. You have already used this exploratory way of writing programs with the first application.
The workflow is to build up programs incrementally instead of running all code at once. The REPL is a perfect place to try out snippets and experiment with different aspects of F#.
In the preceding example code, we saw that parsing data from a file and extracting various information is straightforward, and results in code that is both easy to read and understand. That's one of the highlights of F#, not less important in quantitative finance where code can be complex and hard to follow and comprehend in many languages.
Let's illustrate the preceding statements with another example. The data in the CSV file in the previous sample application was sorted with the most recent date first. If we want the data to be ordered in a more natural way, lowest date first and so on, we can simply reverse the entire list in the following way:
/// Reverses the price data from the CSV-file let reversePrices = openFile filePath |> List.map splitCommas |> List.rev
Suppose we are interested in parsing the date column in the example with the stock prices. The entire row looks something like the following:
[|"2013-02-22"; "54.96"; "55.13"; "54.57"; "55.02"; "5087300"; "55.02"|]
We are interested in the first column, with index 0:
lowestVolume.[0];; val it : string = "2013-02-22"
We can make use of the .NET classes for date and time in the System.DateTime
namespace:
> let dateTime = System.DateTime.ParseExact(lowestVolume.[0], "yyyy-mm-dd", null);; val dateTime : System.DateTime = 2013-01-22 00:02:00
Now we have a System.DateTime
object, which is compatible with C# and the other .NET languages, to work with!
In this chapter, we had a look into the basics of programming in F# using Visual Studio. We covered a wide variety of the language and scratched the surface of functional programming, where immutability plays a key role. Throughout the chapter we started out illustrating some of F#'s language features and how to make use of the .NET framework. At the end of the chapter, we put together a simple application which shows the power and elegant syntax of F#. Functions are the main building block in any functional programming language. Building new functions from existing ones is a way of abstracting away the complexity and allows for reuse.
In the next chapter, we'll dive into more details about the F# language. You'll learn more about data structures, such as Lists, Sequences, and Arrays. You'll also learn how to structure your program using modules and namespaces, things that will become useful in larger programs. The next chapter will also introduce you to threads, thread pools, asynchronous programming using .NET, and language-specific constructs for the F# language.