Design Patterns in F#

Steve Goguen

Who am I?

Design Patterns

  • A general reusable solution to a commonly occurring problem
  • Not a finished design that can be transformed directly into code
  • Description or template for how to solve a problem that can be used in many different situations

Peter Norvig noted that many of the 23 design patterns were significantly simplified or eliminated with higher level languages.

…if thought corrupts language, language can also corrupt thought.
George Orwell

Does it really?

Read All Files From a Folder

Comparing Apples to Oranges

Functional F#

let ReadFolder = Directory.GetFiles >> Array.map File.ReadAllText

Imperative C#

string[] ReadFolder(string path) {
  var filenames = Directory.GetFiles(path);
  var results = new string[filenames.Length];
  for(var i = 0; i < results.Length; i++) {
    var filename = filenames[i];
    var text = File.ReadAllText(filename);
    results[i] = text;
  }
  return results;
}

Could ceremony and painstaking attention to the details corrupt your thinking and distract you from solving your problem?

Read All Files From a Folder

A Better Comparison

Functional C#

string[] ReadFolder(string path) {
  return Directory.GetFiles(path).Select(f => File.ReadAllText(f)).ToArray();
}

Functional F#

let ReadFolder = Directory.GetFiles >> Array.map File.ReadAllText

Read All Files From a Folder

A Better Comparison

Functional C# with LINQ

string[] ReadFolder(string path) {
  return (from f in Directory.GetFiles(path)
          select File.ReadAllText(f)).ToArray();
}

Functional F#

let ReadFolder = Directory.GetFiles >> Array.map File.ReadAllText

The F# Version - Looking under the hood

The F# Version - Looking under the hood

Functional Programming Guidelines

Favor pure functions

Real programs aren't pure

Pure Function

let add x y = x + y

Not Pure Functions

Directory.GetFiles - // Different output for same input
System.IO.File.WriteAllText - // Affects the outside world

Referential Transparency

let add x y = x + y
let z = add 4 5

The function's body can be substituted like so:

let z = 4 + 5

Not Referential Transparent

let files = Directory.GetFiles(@"C:\Test")
let files1 = files
let files2 = files

Substitution changes behavior

let files1 = Directory.GetFiles(@"C:\Test")
let files2 = Directory.GetFiles(@"C:\Test")

Define Variables

Function - Partial Application

Functions - Currying vs. Tuples

Functions - In C#


Think of these as...
let add x y = x + y
let add2 = fun x y -> x + y
let add3 = fun x -> fun y -> x + y

Translated to C#
Func<int,Func<int,int>> Add = (x => {
  return y => {
    return x + y;
  };
});

Function with a Body


More Compositional


Filtering Sequences - IEnumerable

  1. We can many code patterns into functions

Higher Ordered Functions

Higher Ordered Functions

Higher Ordered Functions

Caching Made Simple

Lazy Type

Lists

Classes

The Interpreter Pattern - Enums on Steriod

Pattern Matching

Pattern Matching

Pattern Matching

Active Patterns

Aync Builder

What Builders are Made of...

Type Notation

'a Support for simple types
'a -> 'b Functions
'a * 'b Tuples
'a + 'b Discriminated Unions

A Few Examples

Example Meaning
string A string type
string * int A tuple. Example ("Hello", 5)
string + int Something that can either be a string or an integer, but not both.
Note: Syntax not supported in F#
string -> int A function that takes a string and returns an integer
unit -> int A function that takes a nothing and returns an integer
string -> unit A function that takes a string and returns nothing
'a A generic type
'a -> 'a A function that accepts any type of object and returns an object of the same type
('a -> 'b) -> (List<'a> -> List<'b>) A function that takes any function (from 'a to 'b) and returns a function that a List<'a> and returns a List<'b>

Type Notation Revisited

'a Support for simple types
'a -> 'b Functions
'a * 'b Tuples
'a + 'b Discriminated Unions

This assumes we're talking about pure functions

Non-pure Functions

'a -> IO<'b> Let's wrap non-pure values in an IO type

Enumerating All Possible Types

'a A value
() -> 'a Function - no args returning a value
() -> IO<'a> Function returning some input
'a -> 'a A pure function returning its own type
'a -> 'b A regular function
'a * 'b Tuples
'a + 'b Discriminated Unions

This assumes we're talking about pure functions


How can we account for side-effects in types?

Let's Talk About Patterns and Names



...but first, which square is a different color?

One is clearly dumbu and the others are borou.


Don't feel too bad, this test stumps the Himba people.

Do names define rigid mental boundaries?

Hierarchical Temporal Memory


Let's train our brains to distort our vision


Enough About Brains - Let's Talk About Patterns

Do these names distort our perspective?

  • Factory Method
  • Interpreter
  • Adaptor
  • Singleton
  • Lazy Initialization
  • Prototype
  • Abstract Factory
  • Multiton
  • Decorator
  • Builder
  • Composite
  • Façade
  • Chain of Responsibility
  • Command
  • Front Controller
  • Flyweight
  • Proxy
  • Blackboard
  • Iterator
  • Mediator
  • Memento
  • Null Object
  • Observer
  • Servant
  • Specification
  • State
  • Strategy
  • Template Method
  • Visitor

The Factory Pattern - In C#

Define an interface for creating an object, but let the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses.

Step 1: Define our interfaces

interface IEmployee {
  string Name { get; }
  decimal GetCheckAmount();
}

interface IEmployeeFactory {
  IEmployee Create(string name, string position);
}

The Factory Pattern - Step 2: Implement IEmployee

public class SalariedEmployee : IEmployee {
  //  Our private fields to store our values
  private string name;
  private decimal yearlySalary;
  
  // Constructor
  internal SalariedEmployee (string name, decimal yearlySalary) {
    this.name = name;
    this.yearlySalary = yearlySalary;
  }

  // Readonly Name property
  string IEmployee.Name {
    get { return name; }
  }

  // Function to calculate a check amount
  decimal IEmployee.GetCheckAmount() {
    return yearlySalary / 52;
  }
}

The Factory Pattern - Step 3: Implement a Factory

Create a class implementing IEmployeeFactory

public class SalariedEmployeeFactory : IEmployeeFactory {
  public IEmployee Create(string name, string position) {
    if(position == "boss")
      return new SalariedEmployee(name, 300000);
    return new SalariedEmployee(name, 40000);
  }
}

Step 4: Try out our new factory

public class FactoryExamples {
  public void Example1() {
    IEmployeeFactory f = new SalariedEmployeeFactory();
    IEmployee[] Employees = new IEmployee[] {
      f.Create("Larry", "stooge"),
      f.Create("Curly", "stooge"),
      f.Create("Moe", "boss")
    };

  }
}

All that code just to create some lousy objects??? Really???

Factories in F#

Step 1: Define the Employee interface (The factory will just be a function)

type IEmployee =
  abstract member Name: string
  abstract member GetCheckAmount: unit -> decimal

Step 2: Create a function returning an object implementing IEmployee

let createSalaryEmployee(name,position) = 
  let yearlySalary = if position = "boss" then 300000.0m else 40000.0m
  { new IEmployee with
    member this.Name = name
    member this.GetCheckAmount() = yearlySalary / 52.0m }

Step 3: Try out our constructor

//  Let's try this out
let employee = createSalaryEmployee
let employees = [
  employee("Larry", "stooge") 
  employee("Curly", "stooge")
  employee("Moe", "boss")
]
Lesson: Simple factories are just functions we can swap out!

The Singleton Pattern

C# Version

class A {
  public private A() {}
  private static A instance = new A();
  public static A Instance() {
    return 
  }
  public void Action() {
    Console.Write("action");
  }
}

Tao Liu's Version

type A private () =
    static let instance = A()
    static member Instance = instance
    member this.Action() = printfn "action"

let DesignPattern1() = 
    let a = A.Instance;
    a.Action()
;

The Functional Singleton

F# C#
let makeSingleton f =
  let instance = f()
  fun () -> instance
Func<A> makeSingleton<A>(Func<A> f) {
  var instance = f();
  return () => instance;
}
  • Constructors are just functions
  • Singletons are just constructors that return the same instance
  • Using first class functions, closures and higher ordered types there's no need to repeat this pattern throughout your code.

Let's try it out!

The Decorator Pattern

Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality.

public interface Coffee {
    public double getCost();
    public String getIngredients();
}

public class SimpleCoffee : Coffee {
    public double getCost() { return 1; }
    public String getIngredients() { return "Coffee"; }
}

public class Milk : Coffee {
    private Coffee decoratedCoffee;
    public Milk(Coffee decoratedCoffee) {
        this.decoratedCoffee = decoratedCoffee;
    } 
    public double getCost() { // overriding methods defined in the abstract superclass
        return decoratedCoffee.getCost() + 0.5;
    }
    public String getIngredients() {
        return decoratedCoffee.getIngredients() + ingredientSeparator + "Milk";
    }
}

The Decorator Pattern - In F#

type Coffee =
  abstract member getCost: unit -> double
  abstract member getIngredients: unit -> string

let SimpleCoffee = 
  { new Coffee with 
    member this.getCost() = 1.0
    member this.getIngredients() = "Coffee"
  }

let Milk(coffee:Coffee) =
  { new Coffee with 
    member this.getCost() = coffee.getCost() + 0.5
    member this.getIngredients() = coffee.getIngredients() + ", " + "milk"

Let's try this out

let myCoffee = Milk(SimpleCoffee)

We could pipe it too

let myCoffee = SimpleCoffee |> Milk

The Builder Pattern - In C#

Separate the construction of a complex object from its representation allowing the same construction process to create various representations.

class CoffeeBuilder {
  protected Coffee coffee;

  public Coffee getCoffee() {
      return coffee;
  }
 
  public void createNewCoffeeProduct() {
    coffee = new SimpleCoffee();
  }
 
  public void addMilk() {
    coffee = new Milk(coffee);
  }
}
// Usage
var builder = new CoffeeBuilder();
builder.createNewCoffeeProduct();
builder.addMilk();
var myCoffee = builder.getCoffee();

The Builder Pattern - In C# Using Fluent Interface

class CoffeeBuilder {
  protected Coffee coffee;

  public Coffee getCoffee() {
      return coffee;
  }
 
  public CoffeeBuilder createNewCoffeeProduct() {
    coffee = new SimpleCoffee();
    return this;
  }
 
  public CoffeeBuilder addMilk() {
    coffee = new Milk(coffee);
    return this;
  }
}
// Usage
var myCoffee = new CoffeeBuilder()
  .createNewCoffeeProduct().addMilk().getCoffee();

The Builder Pattern - In F# Using Fluent Interfaces

Define our fluent builder

type CoffeeBuilder(coffee:Coffee) = 
  new() = CoffeeBuilder(SimpleCoffee)
  member this.addMilk() = CoffeeBuilder(Milk(coffee))
  member this.getCoffee() = coffee

Use it

let builder = new CoffeeBuilder()
let myCoffee = builder.addMilk().getCoffee()

The Builder Pattern - In F#

Why create a fluent interface when you can just pipe constructors?

let myCoffee = SimpleCoffee |> Milk

Do F# Sequences use the Builder Pattern?

let myFunctions =
  let path = @"C:\Project"
  Directory.EnumerateFiles(path, "*.vb", SearchOption.AllDirectories)
  |> Seq.map File.ReadAllText
  |> Seq.collect (fun text -> Regex.Matches(text, "Function \w+\([^)]*\)") |> Seq.cast
 )
  |> Seq.map (fun m -> m.Value)
  |> Seq.take 10  

Design Patterns Translated

Pattern Type
Singleton - Ensure a class has only one instance, and provide a global point of access to it. () -> 'a
Factory Method 'a -> 'b
Lazy Initialization - Delay creating an until the first time it's needed () -> 'a
Prototype - Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype 'a -> () -> 'a
Adaptor - Convert the interface of a class into another interface clients expect. 'a -> 'b
Abstract Factory - Provide an interface for creating families of related or dependent objects ('a -> 'b) * ('c -> 'd)
Multiton - Ensure a class has only named instances, and provide global point of access to them string -> 'a

Design Patterns Translated

Pattern Type
Decorator - Attach additional responsibilities to an object dynamically keeping the same interface. 'b -> 'a -> 'a
Builder 'a -> 'b -> 'c
Composite List<'a> -> 'a

Abstraction

In computing science, more abstract viewpoints are often more useful, because of the need to achieve independence from the often overwhelmingly complex details of how things are represented or implemented.
Joseph Goguen

Abstraction

  • Things that appear to be different things may in fact have the same abstract structure
  • Removing metaphical names and labels helps identify these correlations
  • Tools: Category theory, type theory

Contact Info

Free F# Resources