Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
Multiple items
type AllowNullLiteralAttribute =
  inherit Attribute
  new : unit -> AllowNullLiteralAttribute
  new : value:bool -> AllowNullLiteralAttribute
  member Value : bool

Full name: Microsoft.FSharp.Core.AllowNullLiteralAttribute

--------------------
new : unit -> AllowNullLiteralAttribute
new : value:bool -> AllowNullLiteralAttribute
Multiple items
val double : value:'T -> double (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.double

--------------------
type double = System.Double

Full name: Microsoft.FSharp.Core.double
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State

Full name: Microsoft.FSharp.Collections.List.fold
Multiple items
val decimal : value:'T -> decimal (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.decimal

--------------------
type decimal = System.Decimal

Full name: Microsoft.FSharp.Core.decimal

--------------------
type decimal<'Measure> = decimal

Full name: Microsoft.FSharp.Core.decimal<_>

Introduction to F#
and functional programming
for the C# developer







Nicola Iarocci
@nicolaiarocci

Disclaimer

This talk is based on the excellent work by







Scott Wlaschin
fsharpforfunandprofit.com | @ScottWlaschin

The goal of this talk

Is just to demistify F# a bit

You can't become an expert in an hour

I'll try not to be too biased

Is it worth the effort learning a new language?

A language that doesn't affect the way you think about programming, is not worth knowing - Alan Perlis

  • You dont really want to invest your time into learning new, marginally important things
  • F# is a language that will change the way you think about programming
  • Don’t trust people who have never looked at it. Don’t bother.

About F#


  • Cross platform (Linux, macOS, Windows)
    • Works with VS Code and other editors
  • Very active and friendly community
    • Start with fsharp.org
    • F# Slack channel
    • #fsharp on Twitter

Differences between C# and F#

From least to most important


  • Concise syntax
  • Type inference
  • Different defaults
  • Different philosophy

Unique to F#


  • Functional first
  • Algebraic type system
  • Interactivity

SYNTAX

Immutable C# class we'll use as an example

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
public class Person 
{
    public Person(string name, DateTime birthday)
    {
        _name = name;
        _birthday = birthday; 
    }

    private readonly string _name;
    private readonly DateTime _birthday;

    public string Name
    {
        get { return _name; }
    }
    public DateTime Birthday 
    {
        get { return _birthday; }
    }
}

Indentation instead of curly braces

Do we really need the curly braces?

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
public class Person 
{
    public Person(string name, DateTime birthday)
    {
        _name = name;
        _birthday = birthday; 
    }

    private readonly string _name;
    private readonly DateTime _birthday;

    public string Name
    {
        get { return _name; }
    }
    public DateTime Birthday 
    {
        get { return _birthday; }
    }
}

Indentation gives us all the clues we need

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
public class Person =

    public Person(string name, DateTime birthday) =

        _name = name;
        _birthday = birthday; 

    private readonly string _name;
    private readonly DateTime _birthday;

    public string Name = 

        get { return _name; }

    public DateTime Birthday =

        get { return _birthday; }

Use '=' to start blocks

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
public class Person =

    public Person(string name, DateTime birthday) =

        _name = name;
        _birthday = birthday; 

    private readonly string _name;
    private readonly DateTime _birthday;

    public string Name = 

        get { return _name; }

    public DateTime Birthday =

        get { return _birthday; }

Shift up to use less vertical space

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
public class Person =

    public Person(string name, DateTime birthday) =
        _name = name;
        _birthday = birthday; 

    private readonly string _name;
    private readonly DateTime _birthday;

    public string Name = 
        get { return _name; }

    public DateTime Birthday  =
        get { return _birthday; }

Python's use of whitespace stopped feeling unnatural after about twenty minutes. I just indented code, pretty much as I would have done in a C program anyway, and it worked - Eric S. Raymond

Automatically create backing fields from constructor parameters

A lot of duplication...

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
public class Person =

    // constructor arguments match the private fields
    public Person(string name, DateTime birthday) =
        // arguments values are assigned to backing fields
        _name = name;
        _birthday = birthday; 

    // private fields declared
    private readonly string _name;
    private readonly DateTime _birthday;

    public string Name = 
        // field value finally returned
        get { return _name; }

    public DateTime Birthday  =
        // field value finally returned
        get { return _birthday; }

Use the constructor params directly

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
public class Person =

    public Person(string name, DateTime birthday) =
        ...

    public string Name = 
        get { return name; }

    public DateTime Birthday =
        get { return birthday; }

Merge the primary constructor with the class definition

How often do you have more than one constructor?

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
public class Person =

    // enquiring minds want to know
    public Person(string name, DateTime birthday) =
        ...

    public string Name = 
        get { return name; }

    public DateTime Birthday =
        get { return birthday; }

Why not merge the constructor with the class definition?

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public class Person(string name, DateTime birthday) =

    public string Name = 
        get { return name; }

    public DateTime Birthday =
        get { return birthday; }

Reduced syntax noise

Class is immutable, every property is 'get' only

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public class Person(string name, DateTime birthday) =

    public string Name = 
        get { return name; }

    public DateTime Birthday =
        get { return birthday; }

'get' is gone!

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public class Person(string name, DateTime birthday) =

    public string Name = 
        return name; 

    public DateTime Birthday =
        return birthday;

Who even likes typing semicolons anyway?

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public class Person(string name, DateTime birthday) =

    public string Name = 
        return name; 

    public DateTime Birthday =
        return birthday;

Semicolons gone!

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public class Person(string name, DateTime birthday) =

    public string Name = 
        return name

    public DateTime Birthday =
        return birthday

Implicit 'return'

F# is an expression-oriented language

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public class Person(string name, DateTime birthday) =

    public string Name = 
        return name

    public DateTime Birthday =
        return birthday

The return is implict for the last line in a block

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public class Person(string name, DateTime birthday) =

    public string Name = 
        name

    public DateTime Birthday =
        birthday

Members are public by default

The properties are immutable

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public class Person(string name, DateTime birthday) =

    public string Name = 
        return name

    public DateTime Birthday =
        return birthday

No explicit 'public' is needed

1: 
2: 
3: 
4: 
5: 
6: 
7: 
class Person(string name, DateTime birthday) =

    string Name = 
        return name

    DateTime Birthday =
        return birthday

Type Inference

We have to repeat the type? Can't the compiler figure it out?

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
class Person(string name, DateTime birthday) =

    // it's a string, we know from the constructor 
    string Name = 
        name

    // and this one, it ought to be a DateTime
    DateTime Birthday =
        birthday

The answer is a big YES!

1: 
2: 
3: 
4: 
5: 
6: 
7: 
class Person(string name, DateTime birthday) =

    Name = 
        name

    Birthday =
        birthday

Class and method members

the 'member' keyword indicates class members

1: 
2: 
3: 
4: 
5: 
6: 
7: 
class Person(string name, DateTime birthday) =

    member this.Name = 
        name

    member this.Birthday =
        birthday

'member' also defines methods

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
class Person(string name, DateTime birthday) =

    member this.Name = 
        name

    member this.Birthday =
        birthday
    
    member this.Age() =
        var daysDiff = DateTime.Today.Subtract(birthday).Days
        daysDiff / 365

Type annotations

In C#, types come before name

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
// all C-derivative languages use type-then-name syntax
class Person(string name, DateTime birthday) =

    member this.Name = 
        name

    member this.Birthday =
        birthday
    
    member this.Age() =
        var daysDiff = DateTime.Today.Subtract(birthday).Days
        daysDiff / 365

In F#, types come after name

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
// most modern languages adopt type-then-name instead
class Person(name: string, birthday: DateTime) =

    member this.Name = 
        name

    member this.Birthday =
        birthday
    
    member this.Age() =
        var daysDiff = DateTime.Today.Subtract(birthday).Days
        daysDiff / 365

Different keywords

In C#, we use 'class' and 'var'

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
// 'class' identifies - you guessed it - a class
class Person(name: string, birthday: DateTime) =

    member this.Name = 
        name

    member this.Birthday =
        birthday
    
    member this.Age() =
        // 'var' is C#'s type-inference declaration
        var daysDiff = DateTime.Today.Subtract(birthday).Days
        daysDiff / 365

In F#, we use 'type' and 'let'

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
// 'type' identifies - you guessed it again! - F# types
type Person(name: string, birthday: DateTime) =

    member this.Name = 
        name

    member this.Birthday =
        birthday
    
    member this.Age() =
        // 'let' is how you declare stuff in F#'s
        let daysDiff = DateTime.Today.Subtract(birthday).Days
        daysDiff / 365

Modern C# equivalent with auto-properties and expression-bodied members

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
class Person(string name, DateTime birthday) 
{
    public string Name { get; } = name;

    public DateTime Birthday { get; } = birthday;

    public int Age() => DateTime.Today.Subtract(birthday).Days / 365;
}

// Actually, this does not work. Primary constructors was 
// a short-lived feature that only existed in VS2014
  • surprising similar
  • not a coincidence

Functional approach: separate the data from the functions

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
type Person = {
    Name: string
    Birthday: DateTime
    }

let age person =
    let daysDiff = DateTime.Today.Subtract(person.Birthday).Days
    daysDiff / 365

// age : Person -> int 
// (the inferred type of the function)

Observation

Syntax is never the most important thing about a programming language. But...

21 lines of code has shrunk to 5 lines of code

You write 1/3 as much code

Type inference

1: 
2: 
3: 
4: 
5: 
let doSomething f x =
    let y = f (x + 1)
    "hello" + y

// two parameters: f & x
1: 
2: 
3: 
let doSomething f x =
    let y = f (x + 1)   // x must be an int
    "hello" + y
1: 
2: 
3: 
let doSomething f x =
    let y = f (x + 1)
    "hello" + y         // y must be a string
1: 
2: 
3: 
let doSomething f x =
    let y = f (x + 1)   // f must be a int -> string function
    "hello" + y

Inferred type of doSomething

1: 
2: 
3: 
4: 
5: 
f:(int -> string) -> x:int -> string

// 'f' is a function, it takes an int and returns a string;
// 'x' is an int;
// return value is a string

A more complex example

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
// C# code
public IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(
    IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector
    )
{
...
}


// F# code
let GroupBy source keySelector = 
    ...

Benefits of type inference





- less typing
- less noise
- more logic

Different defaults

F# has different defaults


  • Immutable by default
    • mutable is a special case
  • Non-null types/classes by default
    • nullable is a special case
  • Structural equality by default
    • reference equality is a special case
  • Everything must be initialized
    • explicit is better than implicit - Zen of Python

Immutability by default

1: 
2: 
3: 
let x = 1

x <- 2  // Error: this value is not mutable

Immutability by default

1: 
2: 
3: 
let mutable x = 1

x <- 2  // OK

Not nullable by default

1: 
2: 
3: 
4: 
5: 
6: 
type Person = {
    Name: string
    Birthday: DateTime
    }

let x : Person = null   // Error: the type Person does not have null as a proper value

Not nullable by default

1: 
2: 
3: 
4: 
5: 
6: 
7: 
[<AllowNullLiteralAttribute>]
type Person(name: string, birthday: DateTime) =
    member this.Name = name
    member this.Birthday = birthday


let x : Person = null   // OK
1: 
// more pain intended

Structural equality

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
type Person = {
    Name: string
    Birthday: DateTime
    }

let bday = DateTime(1980,1,1)
let alice1 = {Name="Alice"; Birthday=bday}
let alice2 = {Name="Alice"; Birthday=bday} 

alice1 = alice2     // true or false?
1: 
2: 
// true.
// never write GetHashCode $ Equals ever again!

Everything must be initialized

1: 
2: 
3: 
4: 
5: 
6: 
type Person = {
    Name: string
    Birthday: DateTime
    }

let alice : Person          // Error

Everything must be initialized

1: 
2: 
3: 
4: 
5: 
6: 
type Person = {
    Name: string
    Birthday: DateTime
    }

let alice = {Name="Alice"}  // Error: Birthday is required

Everything must be initialized

1: 
2: 
3: 
4: 
5: 
6: 
type Person = {
    Name: string
    Birthday: DateTime
    }

let alice = {Name="Alice"; Birthday=DateTime(1980,1,1)}  // OK

Different philosophy

Different philosophy

  • C# historically comes from C-like approach
  • F# come from ML, a MetaLanguage for proving things

Goal: Predictable code

  • Can you understand the code using only the information that you have right in front of you?
  • You're not allowed to delve into other parts of the codebase.

Tricky question

1: 
2: 
3: 
4: 
var x = 1;
DoSomething(x);

var y = "hello" + x;    // What value is y?
1: 
// The answer is "hello world".

Tricky question

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
function DoSomething (foo)  { x = "world"; }

var x = 1;
DoSomething(x);

var y = "hello" + x;    

// The code is actually JavaScript.
1: 
// Thanks to static typing, this can never happen in C#.

Predictable language

  • Variables should not be allowed to change their type
1: 
2: 
3: 
4: 
5: 
// create two customers
var cust1 = new Customer(99, "J Smith");
var cust2 = new Customer(99, "J Smith");

cust1 == cust2;     // true or false?
1: 
// You can't tell. Not predictable.

Predictable language

  • Variables should not be allowed to change their type
  • Objects with the same values should be equal by default.
1: 
2: 
3: 
4: 
5: 
// create a customer and an order
var cust = new Customer(99, "J Smith");
var order = new Order(99, "J Smith");

cust.Equals(order);     // true or false?
1: 
2: 
// This is probably a bug.
// Why are you attempting to compare an order to a customer?

Predictable language

  • Variables should not be allowed to change their type
  • Objects with the same values should be equal by default.
  • Comparing objects of different types is a compile-time error.
1: 
2: 
3: 
4: 
// create a customer
var cust = new Customer();

Console.WriteLine(cust.Address.Country)  // what is the expected output?
1: 
2: 
// You can't tell. Not predictable.
// What if Address is not always required?

Predictable language

  • Variables should not be allowed to change their type
  • Objects with the same values should be equal by default.
  • Comparing objects of different types is a compile-time error.
  • Objects must always be initialized to a valid state. Not doing so is a compile-time error.
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
// create a customer
var cust = new Customer(99, "J Smith");

// add it to a set
var processedCustomers = new HashSet<Customer>();
processedCustomers.Add(cust);

// process it
ProcessCustomer(cust);

processedCustomers.Contains(cust);  // true or false?
1: 
2: 
// You can't tell. Not predictable.
// If ProcessCustomer mutates the customer, it might change the hash
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
// create a customer
var cust = new ImmutableCustomer(99, "J Smith");

// add it to a set
var processedCustomers = new HashSet<ImmutableCustomer>();
processedCustomers.Add(cust);

// process it and return the changes
var changedCustomer = ProcessCustomer(cust);

processedCustomers.Contains(cust);  // true or false?
1: 
2: 
3: 
// true.
// Immutability forces changed values to be returned explicitly.
// Original value unchanged.

Predictable language

  • Variables should not be allowed to change their type
  • Objects with the same values should be equal by default.
  • Comparing objects of different types is a compile-time error.
  • Objects must always be initialized to a valid state. Not doing so is a compile-time error.
  • Once created, objects and collections must be immutable.
1: 
2: 
3: 
4: 
5: 
6: 
7: 
// create a repository
var repo = new CustomerRepository();

// find a customer by id
var customer = repo.GetById(42);

Console.WriteLine(customer.Id); // what is the expected output?
1: 
2: 
3: 
// You can't tell. Not predictable.
// What happens if the customer is missing?
// Is the customer null or what?
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
// create a repository
var repo = new CustomerRepository();

// find a customer by id
var customerOrError = repo.GetById(42);

// handle both cases
if (customerOrError.IsCustomer)
    Console.WriteLine(customerOrError.Customer.Id);

if (customerOrError.IsError)
    Console.WriteLine(customerOrError.ErrorMessage);

// Instead, build error cases into the return type.

Predictable language

  • Variables should not be allowed to change their type
  • Objects with the same values should be equal by default.
  • Comparing objects of different types is a compile-time error.
  • Objects must always be initialized to a valid state. Not doing so is a compile-time error.
  • Once created, objects and collections must be immutable.
  • Missing data or errors must be made explicit. No nulls allowed.

Predictable language

  • Variables should not be allowed to change their type
  • Objects with the same values should be equal by default.
  • Comparing objects of different types is a compile-time error.
  • Objects must always be initialized to a valid state. Not doing so is a compile-time error.
  • Once created, objects and collections are generally immutable.
  • Missing data or errors are generally made explicit. Nulls are a code smell.

Predictable language

F# is not perfect. But conventions lead this way.

Functional first

Core principles of functional programming

  • Functions
  • Composition
  • Parameterization

Principle

Functions are things

  • Functions are standalone things
  • Hence, they are not attached to a class
  • Functions can be used for inputs and outputs of other functions
  • From this simple foundation we can build complex things

Principle

Functions can be composed together

Composition in F#

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let add1 x = x + 1
let double x = x + x

let add1_double = 
    add1 >> double

let x = add1_double 5   // Outputs 12

Composition in F#

1: 
2: 
3: 
4: 
let add1_double_square = 
    add1 >> double >> square

let x = add1_double_square 5    // Outputs 144

Composition in C#

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
Func<int, int> add1 = x => x + 1;
Func<int, int> doubl = x => x + x;
Func<int, int> square = x => x * x;

var composed = 
    add1.Compose(doubl).Compose(square);

composed(5);

Nesting functions

1: 
2: 
3: 
4: 
5: 
add1 5                    // = 6
double (add1 5)           // = 12
square (double (add1 5))  // = 144

// Standard way of nesting function calls can be confusing if too deep

Piping in F# (|>)

1: 
2: 
3: 
4: 
5: 
6: 
5 |> add1                     // = 6
5 |> add1 |> double           // = 12
5 |> add1 |> double |> square // = 144

// Pipe operator pipes a value through a set of functions 
// in sequence and returns the resulting value


Piping in C# (if we really wanted to)

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
Func<int, int> Add1 = x => x + 1;
Func<int, int> Double = x => x + x;
Func<int, int> Square = x => x * x;

5.Pipe(Add1);                           // = 6
5.Pipe(Add1).Pipe(Double);              // = 12
5.Pipe(Add1).Pipe(Double).Pipe(Square); // = 144

// Pipe() is a helper method we have to write ourselves

Why we say F# is "functional first"

  • F# makes functional programming easy
  • C# makes FP possible
    • but it's awkward and not idiomatic

Principle

Parameterization

1: 
2: 
3: 
let printList() = 
    for i in [1..10] do
        printfn "the number is %i"
1: 
// [1..10] is hard-coded data. Yuck!
1: 
2: 
3: 
let printList aList = 
    for i in aList do
        printfn "the number is %i" i
1: 
2: 
// It's second nature to parameterize the data input
// However...
1: 
2: 
3: 
4: 
5: 
6: 
let printList aList = 
    for i in aList do
        printfn "the number is %i" i

// Hard-coded behaviour. Yuck!
// FPers would parameterize the action as well.
1: 
2: 
3: 
let printList anAction aList = 
    for i in aList do
        anAction i
1: 
2: 
// We've decoupled the behavior from the data. 
// Any list, any action!
1: 
// F# helps by making this trivial to do.
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
public static int Product(int n)
{
    int product = 1;
    for (int i = 1; i <= n; i++)
    {
        product *= i;
    }
    return product;
}

public static int Sum(int n)
{
    int sum = 0;
    for (int i = 1; i <= n; i++)
    {
        sum += i;
    }
    return sum;
}

// Don't Repeat Yourself (DRY)?
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
public static int Product(int n)
{
    int product = 1;                // Initial value
    for (int i = 1; i <= n; i++)    // Common code
    {
        product *= i;               // Action
    }
    return product;                 // Common code
}

public static int Sum(int n)
{
    int sum = 0;                    // Initial value
    for (int i = 1; i <= n; i++)    // Common code
    {
        sum += i;                   // Action
    }
    return sum;                     // Common code
}

// Don't Repeat Yourself (DRY)?
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let fold action initialValue list =
    let mutable totalSoFar = initialValue 
    for item in list do
        totalSoFar <- action totalSoFar item 
    totalSoFar 

// Initial value dealt with;
// Common code extracted;
// Parameterized action;
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
let product n = 
    let initialValue = 1
    let action productSoFar x = productSoFar * x
    [1..n] |> List.fold action initialValue


let sum n = 
    let initialValue = 0
    let action sumSoFar x = sumSoFar + x 
    [1..n] |> List.fold action initialValue

// Lots of collections functions like this:
// "fold", "map", "reduce", "collect", etc.

Principle

Algebraic type system

F# types can be composed

  • New types are build from smaller types using:
    • AND
    • OR
  • Types are executable documentation
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
type FruitSalad = {
    Apple: AppleVariety
    Banana: BananaVariety
    Cherry: CherryVariety
    }

// FruidSalad = One each of Apple and Banana and Cherry
// Example: pairs, tuples, records (not yet available in C#)
1: 
2: 
3: 
4: 
5: 
6: 
7: 
type Snack =
    | Apple of AppleVariety
    | Banana of BananaVariety 
    | Cherry of CherryVariety

// Snack = Apple or Banana or Cherry
// Not available in C#

We accept three forms of payment: Check, Cash, Card.

  • For Cash we don't need any extra information
  • For Checks we need a check number
  • For Cards we need a card type and card number

How would you implement this?

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
// Possible C# implementation

interface IPaymentMethod 
{..}

class Cash() : IPaymentMethod 
{..}

class Check(int checkNo): IPaymentMethod
{..}

class Card(string cardType, string cardNo) : IPaymentMethod 
{..}
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
// Possible F# implementation

type CheckNumber = int                      // primitive type
type CardNumber = string                    // primitive type

type CardType = Visa | Mastercard           // OR type
type CreditCardInfo = CardType * CardNumber // AND type

type PaymentMethod =                        // OR type
    | Cash
    | Check of CheckNumber                  // can you guess which payment 
    | Card of CreditCardInfo                // methods are accepted?
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
type PaymentAmount = decimal                // primitive type
type Currency = EUR | USD                   // OR type

type Payment = {                            // final type built from many smaller types
    Amount : PaymentAmount
    Currency: Currency
    Method: PaymentMethod
    }

let payment = {                             // usage example
        Amount = PaymentAmount 100.0;
        Currency = EUR;
        Method = Check 9912345
    }
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
type CheckNumber = int                      
type CardNumber = string                    

type CardType = Visa | Mastercard           
type CreditCardInfo = CardType * CardNumber 

type PaymentMethod =                        
    | Cash
    | Check of CheckNumber                  
    | Card of CreditCardInfo                

type PaymentAmount = decimal                
type Currency = EUR | USD                   

type Payment = {                            
    Amount : PaymentAmount
    Currency: Currency
    Method: PaymentMethod
    }

// F# types are executable documentation

Principle

Interactivity demo (if time allows)

Suggested reading

Getting started with F#

Start here

Thank you







Nicola Iarocci

Software Craftsman, MVP
nicolaiarocci.com | @nicolaiarocci