An introduction to F#: Part 1
A few days ago I ran an introduction to F# course for colleagues at work. The idea of the course was to explain the basics of F# including syntax, programming style and the FSI window, as well as to get people excited. I didn’t correctly judge how long the presentation would take (people were asking lots of questions as I went, which is good!), so I ended up splitting it up into two parts. Here I am publishing a rough written copy of the presentation, compiled from my notes that I worked from as I talked, for you to peruse. It’s somewhat rough, but for those who were there it should serve as a reminder. Next time we will talk through some worked examples of Fizzbuzz & fizzbuzzbaz, we will cover types (classes, structs, interfaces, enums, records, units of measure and discriminated unions). We will also go through some application design, such as: MVC, DDD, CQRS, WTFBBQ.
Introduction to F#
What is F#?
It is a strongly typed, multi-paradigm .NET programming language encompassing functional, imperative and object-oriented programming techniques. Although you may have heard that is is ideally suited for number crunching and mathematical computations (and, indeed, it is very good at those) it is in fact a general purpose programming language and can do everything C# can do, and more.
Being .NET you can continue to use all the libraries you’re familiar with, such as NHibernate, Xunit etc.
- Open source, owned by F# Foundation.
- Resources: F# for fun and profit, F# MSDN, F# Foundation
- Pluralsight courses such as Mark Seeman’s A Functional Architecture in F# and Functional Data Structures
The FSI window
The FSI (F Sharp Interactive) window is a window available in all copies of Visual Studio that enables you to load and run arbitrary snippets of F#. You can either type into the window directly or right-click in your code in VS and select “Run in interactive”.
Values and functions
- Defining values: “Binding a name to a value”
let x = 5
- Strongly typed + type inference
- Types can be explicitly stated. “Type annotations”
let x : int = 5
- Defining functions
let square x = x * x
- Notice how everything is an expression, even blocks of code. No ‘return’.
- Type inference *can look ahead* to see how a name is used
-
let printString s = printfn "%s" s
Unit ~= void, but has a value
()
and is a proper type, so List<Unit> is allowed, where List<void> is not.
- Important to learn how to read function signatures, e.g.
int -> int
is equivalent to
Func<int, int>
- Changing how
x
is used will change the type of
x
and therefore the signature of
f
-
let f x = x + " world"
–>
string -> string
- Type annotations rarely used
- How does type inference deal with
let f x = x
? What type is x?
- Call functions with space
let message = f "Hello"
- Functions take exactly one value and return exactly one value.
- Multiple arguments:
- Data structures e.g. classes, tuples
let coords = 4, 5
discuss next
- Currying, discuss later
- Data structures e.g. classes, tuples
Data structures and patterns
- Binding can make use of patterns to bind names to values, “destructuring” / “substructuring”
- Syntax for destructing is same as used for constructing
- Tuples
let coords = 4, 5
and
let x, y = coords
- Can do
let x, y = 4, 5
Optimised away.
- Can be thought of as ad-hoc data structures
- Lists:
-
[]
empty lists
-
[item1; item1]
exact number of items. Note the semicolon; commas are for tuples only
-
item1 :: item2 :: rest
at least n items (in this case at least two)
- More to come after we discuss types
-
- Can nest patterns indefinitely, e.g.
(x, y, z) :: item2 :: rest
- Can use
as
to name higher level patterns
let (x, y) as coords = ...
- Some patterns are complete. e.g.
let x, y =
matches all
int * int
tuples
- Incomplete patterns = compiler warning
let x, 1 = coords
- Patterns can be tested against in
match
expressions
- Like switch but based on patterns: “Pattern matching”
- Primary form of branching in F#
- Example, with explanation of type inference:
-
let rec sumInts list = match list with | [] -> 0 | head :: tail -> head + sumInts tail
- Patterns can be used in function definitions too:
let printCoords (x, y) = ...
this looks a lot like a function that take multiple arguments, but in fact it is one argument that is a tuple.
Currying
- Functions can “take multiple arguments” by either taking a data structure or…
-
let add x y = x + y
-
- Important: read function signature:
int -> int -> int
. Explain.
- Functions are closures.
-
add
is a function that takes an
x
and returns a function that takes a
y
(and has the value of
x
embedded) and returns an int.
- This is the preferred method (but it’s up to you). Why? Partial application!
- Remember: functions only take one argument.
add
takes an int. So what do I get if I call
add 1
?
- What is the signature of
sth
in
let sth = add 1
? –>
int -> int
–>
let inc = add 1
- Any language that has closures also supports currying, E.g. Javascript
var add = function (x){ return function (y) { return x + y; } }var nine = add(4)(5); // => 9 var inc = add(1); var six = inc(5) // => 6
- Remember: functions only take one argument.
- Why don’t people curry in other languages, such as Javascript? Syntax makes it a pain
Control flows
- If statements
- for loops and comprehensions [], [||], seq, range, yield, yield!
- Recursion