Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / F#

How To Learn F#

3.58/5 (7 votes)
21 Feb 2010CPOL5 min read 46.4K  
An article that explains some ways to learn the F# language

Preface

According to those who sought to design a language that uses both imperative and functional programming, F# is a typed functional programming language for the .NET Framework. Yet while functional, or procedural, the F# approach to type inference, object-oriented programming, and dynamic language techniques is substantially different from all other mainstream functional languages. F# is, in a way, typical of today’s programming techniques, because it builds on abstractions. The idea appears to be that the higher you go into abstractions, the farther you get away from the low-level complexities and from error-prone code. The F# CTP Add-In can be installed on Visual Studio 2008 and ships by default on Visual Studio 2010. It is important to note that F# has a strict, orderly mechanism for managing project files. And when installed as an add-in, F# ships a utility called the F# Interactive. This resides on the bottom of the coding platform when you press the Ctrl+Alt+F key combination. When you have that window, you can highlight individual blocks of code by pressing Alt+Enter. The code on the IDE transfers to the F# Interactive window to then execute your code’s functionality. Errors are caught easily this way, especially indentation errors.

You cannot just write your code without indenting sub-routines within a routine. But these code blocks get executed computationally. With other .NET languages like C# and Visual Basic, you have to wait until the entire source file compiles to catch an error or deal with an unhandled exception. So how does one get a solid grasp of this language? F# appears to have different features and uses different building techniques. Well, there is a webcast linked to http://blogs.msdn.com/chrsmith/archive/2008/05/02/f-in-20-minutes-part-i.aspx page at http://channel9.msdn.com/pdc2008/TL11/. This web cast, hosted by Luca Bolognese, starts with an introduction, to then focus on F# code, what it is, and how to use it for practical applications. It is a must. This article will explain the basics of F#: how to write F# code, using scripts that are meant for the interactive so that once they are correct, they can be compiled as a whole file, and how to get a handle on that interactive utility. For instance, consider this basic code snippet:

F#
#light
open System.IO
  let rec allFiles dir =
     Seq.append
         (dir |> Directory.GetFiles)
         (dir |> Directory.GetDirectories |> Seq.map allFiles |> Seq.concat)     

Before examining how this code would appear in the IDE and in the interactive, let’s take a look at the syntax. Much to the surprise of many, there is a main function point of source code execution. The keyword “let” binds a value to a symbol. Because it binds the value rather than assign it, the value cannot be changed. The operator symbol |> is the pipeline operator. Below is a slide that sums up how the pipeline operator works. The ‘dir’ keyword pipelines into GetFiles (that comprise the directory) and then it pipelines into getting the directories. They are then gathered as a sequence of strings and concatenated. One of the reasons why F# is called a typed functional language is that when you hover your cursor over a value, it will tell you its paramterized type. For instance, when the code was highlighted and I pressed Alt+Enter to pass the code into interactive, this was the first statement printed out:

val allFiles : string -> seq<string>

The keyword val for value and allFiles are a string that pipelines (think of almost pushes into) a sequence of strings, which is a recursive listing of directories. After highlighting the code, I pressed Alt-Enter and the code was transferred to the interactive. At the interactive prompt (>), I typed allFiles @”C:\windows\system32”;; (notice the double semi-colons). Here is the output of both passing the code to the interactive and then typing in the function with a directory path as an argument:

F#
val allFiles : string -> seq<string>

> allFiles @"C:\windows\system32";;
val it : seq<string>
= seq
    ["C:\\windows\\system32\\12520437.cpx";
     "C:\\windows\\system32\\12520850.cpx";
     "C:\\windows\\system32\\7B296FB0-376B-497e-B012-9C450E1B7327-2P-0.C7483456-A289-439d- 8115-601632D005A0";
     "C:\\windows\\system32\\7B296FB0-376B-497e-B012-9C450E1B7327-2P-1.C7483456-A289-439d-8115-601632D005A0";
     ...]
>

The keyword val stands for value, and the word rec stands for recursive. When there is no defined type, the default word is ‘it’. Below is a view of the IDE and the F# Interactive:

Capture.JPG

The |> is called the pipeline operator. Besides the keyword let, the piepline operator is one of the most important operators in F#:

Untitled.jpg

WhiteSpace Matters

Other languages, like C#, use semicolons and curly braces to indicate when statements and blocks of code are complete. However, programmers typically indent their code to make it more readable anyway, so these extra symbols often just add syntactic clutter to the code. In F#, whitespace—spaces and newlines—is significant. The F# compiler allows you to use whitespace to delimit code blocks. For example, anything indented more than the if keyword is considered to be in the body of the if statement. Because tab characters can indicate an unknown number of space characters, they are prohibited in F# code. So let’s look at a code example that demonstrates this very fact:

F#
// normally those symbols would not indent to the left, as they are elif and else block
let encode (n: int32) =
    if (n >= 0 && n <= 0x7F) then [ n ]
    elif (n >= 0x80 && n <= 0x3FFF) then [ (0x80 ||| (n >>> 8)) &&& 0xFF; (n &&& 0xFF) ]
    else [ 0xC0; ((n >>> 24) &&& 0xFF);((n >>> 16) &&& 0xFF);((n >>> 8) &&& 0xFF);(n &&& 0xFF) ]

Now we highlight that code, press the Alt+Enter key combination, and we get a type definition prior to inserting encode 32;; and then press enter to obtain the computational result:

F#
> encode 32;;
val it : int32 list = [32]
> encode 320;;
val it : int32 list = [129; 64]
> encode 320000;;
val it : int32 list = [192; 0; 4; 226; 0]
>

F# espouses functional programming with imperative programming. The next example is going to take some data (integers) and then square each element in the sequence in order to sum those squares. I am going to present this example in two constructs in order to explain how F# works.

F#
// the #light directive was taken from the language used to form the design of F#
let data = [1.;2.;3.;4.]

let square x = x * x
let imperativeSum numbers =
    let mutable total = 0.0
    for i in numbers do
        let x = square i
        total <- total + x
    total
let functionalSum numbers =
    numbers
    |> Seq.map square
    |> Seq.sum

When sent to the F# Interactive, we first get:

F#
val data : float list
val square : float -> float
val imperativeSum : seq<float> -> float
val functionalSum : seq<float> -> float

We first notice that despite data being said to be integers, the decimal points to the left of the integers make them floating point integers. So we start with a float list when we bind the value data to that list. When an operation is performed, we use the -> operator (to sort of push them into the operation). This gives us float -> float, and so. The results are as follows:

F#
> imperativeSum;;
val it : (seq<float> -> float) = <clo@0>
> imperativeSum data;;
val it : float = 30.0
> functionalSum data;;
val it : float = 30.0
>

Note how the imperative code block yields results the same as the functional programming block.

History

  • 21st February, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)