F# Journal: The F#.NET Journal: Introduction to F#

The F#.NET Journal: Introduction to F#

1 Year Subscription

All existing articles and 24 new articles

6 Month Subscription

All existing articles and 12 new articles

3 Month Subscription

All existing articles and 6 new articles

Group subscriptions and Site Licences

Get up to 65% off! Read more...

Special Offers

Buy 1 year subscriptions to the OCaml and F#.NET Journals and get 25% off!

+ =

For a limited time only, buy a year subscription to the F#.NET Journal and the F# for Numerics and F# for Visualization libraries and get 25% off!

+ + =

Introduction

Microsoft's F# programming language is a functional language for the .NET framework that was originally developed at Microsoft Research Cambridge by Don Syme. In October 2007, the senior vice president of the developer division at Microsoft announced that F# was being officially productized to become a fully supported .NET language and professional developers were hired to create a team of around ten people to build the product version. In September 2008, Microsoft released the first Community Technology Preview (CTP), an official beta release, of the F# distribution . In December 2008, Microsoft announced that the success of this CTP had encouraged them to escalate F# and it is now will now be shipped as one of the core languages in Visual Studio 2010 , alongside C++, C# 4.0 and VB.

The F# programming language incorporates many state-of-the-art features from programming language research and ossifies them in an industrial strength implementation that promises to revolutionize interactive, parallel and concurrent programming.

Advantages of F#

F# is the world's first language to combine all of the following features:

  • Type inference: types are inferred by the compiler and generic definitions are created automatically.
  • Algebraic data types: a succinct way to represent trees.
  • Pattern matching: a comprehensible and efficient way to dissect data structures.
  • Active patterns: pattern matching over foreign data structures.
  • Interactive sessions: as easy to use as Python and Mathematica.
  • High performance JIT compilation to native code: as fast as C#.
  • Rich data structures: lists and arrays built into the language with syntactic support.
  • Functional programming: first-class functions and tail calls.
  • Expressive static type system: finds bugs during compilation and provides machine-verified documentation.
  • Sequence expressions: interrogate huge data sets efficiently.
  • Asynchronous workflows: syntactic support for monadic style concurrent programming with cancellations.
  • Industrial-strength IDE support: multithreaded debugging, and graphical throwback of inferred types and documentation.
  • Commerce friendly design and a viable commercial market.

Industrial applications

Many companies are already adopting or investigating F# even though it is only available as a Community Technology Preview (CTP) because the language is already useful for certain kinds of applications:

  • Investment banks such as Morgan Stanley, Credit Suisse and UBS already employ hundreds of F# programmers who build in-house software.
  • Companies with products aimed at technical users are already moving to make their APIs F# friendly, such as DataSynapse .
  • Some companies are already shipping software written in F#
  • Authors have already written the books F# for Scientists , Expert F# , Foundations of F# . The books F# in a Nutshell, Programming F# and Functional Programming in .NET are expected to be published in 2009.

Getting started

The latest version of F# at the time of writing is the September 2008 CTP version 1.9.6.2 and is available for free from Microsoft's F# web page .

Microsoft's Visual Studio 2008 is the main development environment for F# programmers and is available for free as Visual Studio Shell (integrated mode) redistributable well as various commercial versions:

The F# development environment for Visual Studio 2008 provides two different modes of execution for F# code:

  • Batch compilation to a .NET executable or DLL.
  • Interactive evaluation.

Articles in The F#.NET Journal quote F# code as it would appear in an interactive session. Specifically, the interactive session provides a > prompt, requires a double semicolon ;; identifier at the end of a code snippet to force evaluation, and returns the names (if any) and types of resulting definitions and values.

For example, the following code is quoted as it would appear in an interactive session, including the result of the calculation and its type:

> 2 + 3;;
val it : int = 5

The remainder of this article describes the basic syntax of the F# programming language.

Comments

The F# language provides two different syntaxes for comments. Comments can be enclosed in (*...*) and may be nested. Alternatively, single-line C-style comments start with // .

The specific style of commenting with /// can be used to autogenerate documentation for a program from comments inside the program. In Visual Studio, Intellisense gives graphical throwback that includes information gleaned from these comments.

For example, the following is an annotated definition of the Fibonacci function:

/// The Fibonacci function
let rec fib = function
  | 0 | 1 as n -> n
  | n -> fib(n-1) + fib(n-2)

The comment appears in the graphical throwback provided by Intellisense:

When developing large projects or libraries, this style of commenting can be used to make the API easier to navigate and explore.

Primitive types

The F# language provides a variety of primitive types, including unit , bool , char , int and float , as well as several compound types such as string .

The unit type serves the purpose of the void type in C, C++, Java and C#. This type indicates a value that conveys no information. The only value of type unit is denoted () .

The following examples demonstrate the most important primitive types:

> ();;
val it : unit = ()
> true;;
val it : bool = true
> 'a';;
val it : char = 'a'
> 3;;
val it : int = 3
> 2.4;;
val it : float = 2.4
> "foobar";;
val it : string = "foobar"

The F# language provides an unusual feature called type inference . This allows the compiler to work out the type of an expresson without requiring explicit annotation. Note that none of the previous examples required any type annotations. This is typical of F# code.

Arithmetic

The numeric types, such as int and float , provide various arithmetic operations:

> 1 + 2 * 3;;
val it : int = 7
> 3.4 + 2.3 * 7.7;;
val it : float = 21.11

Some other types also provide operators. For example, the + operator can be used to concatenate strings:

> "foo" + "bar";;
val it : string = "foobar"

The syntax described so far allows an F# interactive session to be used as a calculator. The language becomes much more useful when functions and compound types, such as tuples, are used to compose programs.

Tuples

The simplest set of inferred types in F# are the tuples. A tuple is simply a compound type composed of a fixed number of other types. Tuples are written as comma-separated values. For example, the pair (2, 3) :

> 2, 3;;
val it : int * int = (2, 3)

Note that the type has been inferred as int * int , denoting a 2-tuple of int values.

Records

User-defined data structures include records and variants. Both are defined with the type declaration. For example, the following declares a record type representing rational numbers:

> type rational = { num: int; denom: int };;
type rational = { num : int; denom : int; }

The fields of a record r are accessed using the syntax r.num . A function to add two of these rational numbers may be written:

> let add p q =
    { num = p.num * q.denom + q.num * p.denom;
      denom = p.denom * q.denom };;
val add_ratio : ratio -> ratio -> ratio = <fun>

For example, 1/3 + 2/5 = 11/15 :

> add_ratio { num = 1; denum = 3 } { num = 2; denum = 5 };;
val it : ratio = {num = 11; denum = 15}

Variants are the other user-defined type.

Variants

The declaration of a variant type lists all possible shapes for values of that type. Each case is identified by a name, called a constructor , that serves both for constructing values of the variant type and inspecting them by pattern-matching. Constructor names are conventionally capitalized to distinguish them from variable names (which start with a lowercase letter).

For example, the following variant type represents a symbolic expression that may contain integers, variables, sums and products:

> type expr =
    | Int of int
    | Var of string
    | Plus of expr * expr
    | Times of expr * expr;;
type expr =
  | Int of int
  | Var of string
  | Plus of expr * expr
  | Times of expr * expr;;

Note that the F# interactive session repeats the type definition.

Values of this type may be constructed using the constructors Int , Var , Plus and Times . For example, the symbolic expression 1 + 2 × y may be expressed as:

> Plus(Int 1, Times(Int 2, Var "y"));;
val it : expr = Plus(Int 1, Times(Int 2, Var "y"))

Variant types are very useful and are used to declare a variety of concrete data structures such as binary trees.

Pattern matching

The utility of variant types lies in the ability to pattern match over them.

The F# language provides two syntaxes for pattern matching. The match ... with construct and the function ... construct. The latter is a special form of function, discussed in the next section.

The match ... with construct evaluates the given expression and then compares the result with a sequence of pattern matches:

match expression with
| pattern -> ...
| pattern -> ...
| pattern -> ...

The expression corresponding to the first pattern that matches is then evaluated and returned.

Patterns may contain primitive values, tuples, records, variant type constructors, variable names and a catchall pattern denoted _ that matches any value.

Subpatterns may contain alternatives, denoted pat | pat . For example, the pattern 1 | 2 matches both 1 and 2.

In the context of the variant type representing a symbolic expression, the following pattern match tests whether the expression e is an atom or a sum/product:

match e with
| Int _ | Var _ -> true
| Plus _ | Times _ -> false

For example, Int 7 is an atomic expression:

> match Int 7 with
  | Int _ | Var _ -> true
  | Plus _ | Times _ -> false;;
val it : bool = true

The use of pattern matching to dissect data structures is much more useful in the context of functions.

Functions and Variables

As F# is a functional programming language, functions are just like any other type. A function that accepts an int and returns a float is said to have the type int -> float .

Functions are declared using one of three different syntaxes. The simplest syntax is fun x y -> ... where x and y are the arguments. For example, a function that doubles its argument:

> fun n -> 2 * n;;
val it : int -> int

This function may be applied to an argument by bracketing the function definition and using the syntax f x to apply x to f :

> (fun n -> 2 * n) 3;;
val it : int = 6

Variables are bound using the syntax let x = 3 . As a function is treated like any other value in F#, a function definition simply binds a function value to a variable name. For example, calling the previous example function "double":

> let double = fun n -> 2 * n;;
val double : int -> int

Note that the information returned by the F# interactive session now contains the name of the variable that has been defined as well as the function type.

Function definitions like this are so short that there is a shorthand notation:

> let double n = 2 * n;;
val double : int -> int

As values in F# programs are often immutable, a variable definition often refers to the previous definition of a variable with the same name. For example, the following computes 2 * 3 - 1 one step at a time, superceding the variable n at each step:

> let x = 2;;
val x : int = 2
> let x = 3 * x;;
val x : int = 6
> let x = x - 1;;
val x : int = 5

However, function definitions often need to refer to themselves ( recursion ). A let binding can be made to refer to itself by adding the rec keyword before the variable name.

The following defines a recursive factorial function:

> let rec factorial n =
    if n=0 then 1 else n * factorial(n - 1);;
val factorial : int -> int

For example, 5! = 120 :

> factorial 5;;
val it : int = 120

The function syntax mentioned earlier is shorthand for a function that pattern matches over its argument. For example, the factorial function may be written more clearly and succinctly as:

> let rec factorial = function
    | 0 -> 1
    | n -> n * factorial(n - 1);;
val factorial : int -> int

Even with the minimal subset of the F# language covered so far, it is already possible to write some interesting example programs.

Example

A function to raise a floating-point number x to the power of an integer n may be written:

> let rec pow n x =
    match n with
    | 0 -> 1.
    | n -> x * pow (n - 1) x;;
val pow : int -> float -> float

For example, 3 4 = 81 :

> pow 4 3.;;
val it : float = 81.0

This function used pattern matching and recursion but also made use of a more subtle feature known as currying . A curried function is a function that returns another function. In this case, the result of pow n is a function that raises the given floating-point number to the power of n .

So the pow function can be used to define more specialized functions such as square and cube by partially applying pow with n alone:

> let square = pow 2;;
val square : float -> float
> let cube = pow 3;;
val square : float -> float

For example, 5 2 = 25 :

> square 5.;;
val it : float = 25.0;;

Further Reading

If you want to learn all about Microsoft's exciting new F# programming language then take out a subscription to The F#.NET Journal .