|
|
| Home Page | The F# Journal |
The F#.NET Journal: Introduction to F#
Group subscriptions and Site LicencesGet up to 65% off! Read more...
IntroductionMicrosoft'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:
Industrial applicationsMany 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:
Getting startedThe 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:
Articles in The F#.NET Journal quote F# code as it would appear in an interactive session. Specifically, the interactive session provides a
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
The specific style of commenting with
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
The
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
> 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
> "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. TuplesThe 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
RecordsUser-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
> 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. VariantsThe 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
> 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 matchingThe utility of variant types lies in the ability to pattern match over them.
The F# language provides two syntaxes for pattern matching. The
The
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
Subpatterns may contain alternatives, denoted
In the context of the variant type representing a symbolic expression, the following pattern match tests whether the expression
match e with | Int _ | Var _ -> true | Plus _ | Times _ -> false
For example,
> 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
Functions are declared using one of three different syntaxes. The simplest syntax is
> 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
> (fun n -> 2 * n) 3;; val it : int = 6
Variables are bound using the syntax
> 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
> 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
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
> 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
> 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
So the
> 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 ReadingIf you want to learn all about Microsoft's exciting new F# programming language then take out a subscription to The F#.NET Journal . |
| © Flying Frog Consultancy Ltd., 2007 | Contact the webmaster |