val createLocation : insert:('a -> 'b -> 'c) -> isValid:('b -> bool) -> city:'a -> state:'b -> 'c

Full name: index.createLocation
val insert : ('a -> 'b -> 'c)
val isValid : ('b -> bool)
val city : 'a
val state : 'b
val raise : exn:System.Exception -> 'T

Full name: Microsoft.FSharp.Core.Operators.raise
namespace System
Multiple items
type ArgumentException =
  inherit SystemException
  new : unit -> ArgumentException + 4 overloads
  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
  member Message : string
  member ParamName : string

Full name: System.ArgumentException

--------------------
System.ArgumentException() : unit
System.ArgumentException(message: string) : unit
System.ArgumentException(message: string, innerException: exn) : unit
System.ArgumentException(message: string, paramName: string) : unit
System.ArgumentException(message: string, paramName: string, innerException: exn) : unit
val add : a:int -> b:int -> int

Full name: index.add
val a : int
val b : int
val addFive : (int -> int)

Full name: index.addFive
val log : func:('a -> 'b) -> input:'a -> 'b

Full name: index.log
val func : ('a -> 'b)
val input : 'a
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val result : 'b
type LocationInsert = obj

Full name: index.LocationInsert
val insertLocation : city:'a -> state:'b -> date:'c -> 'd

Full name: index.insertLocation
val date : 'c
val command : LocationInsert
Multiple items
module Option

from Microsoft.FSharp.Core

--------------------
type Option<'T> =
  | Some of 'T
  | None

Full name: index.Option<_>
union case Option.Some: 'T -> Option<'T>
union case Option.None: Option<'T>
val divideBy : bottom:int -> top:int -> Option<int>

Full name: index.divideBy
val bottom : int
val top : int
val insertLocation : obj

Full name: index.insertLocation
val query : Linq.QueryBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.query
module Seq

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U>

Full name: Microsoft.FSharp.Collections.Seq.map

A Developer's journey from OO to Functional

@ReidNEvans

http://reidevans.tech

Tombras

http://www.tombras.com

  • Daimler
  • ESPN
  • McDonalds
  • Michelin

Currently Hiring

  • Front end developer
  • Technical Director
  • QA

Overview

  • OO design patterns
  • SOLID principles
  • Code Bloat
  • Statically type check all the things
  • Staying focused on the happy path
  • Results

The model you use to view the world shapes the thoughts you are able to think.

@TheBurningMonk

My beginnings in professional development

1: 
2: 
3: 
4: 
procedure TForm1.Button1Click(Sender: TObject);
begin
    Label1.Caption := 'Hello World';
end;
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
procedure TForm1.Button1Click(Sender: TObject);
begin
    Locations := TTable.Create(Self);
    with Locations do
    begin
        DatabaseName := 'MyDB';
        TableName := 'MyTable';
        Open;

        Append;
        FieldByName('City').Value := cityEdit.Text;
        FieldByName('State').Value := stateEdit.Text;
        Post;

        Close;          
    end;
end;
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
void button1_Click(object sender, System.EventArgs e)
{
    using (var con = new SqlConnection("MyDb"))
    using (var command = con.CreateCommand())
    {
        command.CommandText = 
            @"Insert into Locations ([State], City)
              Values(@State, @City)";

        command.Parameters.AddWithValue("City", textboxCity.Text);
        command.Parameters.AddWithValue("State", textboxState.Text);  

        con.Open();
        command.ExecuteNonQuery();
    }       
}
What if I want to do the same thing from a different event?
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
void button1_Click(object sender, System.EventArgs e)
{
    CreateLocation();
}

void CreateLocation()
{
    using (var con = new SqlConnection("MyDb"))
    using (var command = con.CreateCommand())
    {
        command.CommandText = 
            @"Insert into Locations ([State], City)
              Values(@State, @City)";

        command.Parameters.AddWithValue("State", tbState.Text);
        command.Parameters.AddWithValue("City", tbCity.Text);  

        con.Open();
        command.ExecuteNonQuery();
    }
}
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
public class LocationsManager  
{
    public void CreateLocation(string city, string state) 
    {
        if (!States.Contains(state)) throw new ArgumentException()

        using (var con = new SqlConnection("MyDb"))
        using (var command = con.CreateCommand()) {
            command.CommandText = 
                @"Insert into Locations ([State], City)
                  Values(@State, @City)";

            command.Parameters.AddWithValue("State", state);
            command.Parameters.AddWithValue("City", city);  

            con.Open();
            command.ExecuteNonQuery();
        }
    }
}
How should all this code fit together?

OO Design patterns

Factory

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
public class Factory
{
    public IFoo GetImplementation(MyEnum enum)
    {
        switch (enum)
        {
            case MyEnum.Foo : return new Foo();
            case MyEnum.Bar : return new Bar();
            default: return null;
        }
    }
}

Strategy

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
public class LocationsManager  
{
    readonly ILocationRepository _locationRepository;

    public LocationsManager(ILocationRepository locationRepository)
    {
        _locationRepository = locationRepository;
    }

    public void CreateLocation(string city, string state)
    {
        _locationRepository.Insert(city, state);
    }
}

Command

1: 
2: 
3: 
4: 
public interface ICommand
{
    void Execute();
}

Design patterns are just terms to define how an abstraction is passed around in code

How do I know if the code I'm writing is any good?

Solid principals

  • Single Responsibility Principal
  • Open Closed Principal
  • Liskov Substitution Principal
  • Interface Segregation Principal
  • Dependency Inversion Principal
Can't I do all of that in Object Oriented code?
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
public class LocationsManager  
{
    public void CreateLocation(string city, string state) 
    {
        if (!States.Contains(state)) throw new ArgumentException()

        using (var con = new SqlConnection("MyDb"))
        using (var command = con.CreateCommand()) {
            command.CommandText = 
                @"Insert into Locations ([State], City)
                  Values(@State, @City)";

            command.Parameters.AddWithValue("State", state);
            command.Parameters.AddWithValue("City", city);  

            con.Open();
            command.ExecuteNonQuery();
        }
    }
}
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
public class LocationsManager  
{
    readonly ILocationRepository _locationRepository;
    readonly IStateValidator _stateValidator;

    public LocationsManager(ILocationRepository r, IStateValidator v)
    {
        _locationRepository = r; 
        _stateValidator = v;
    }

    public void CreateLocation(string city, string state)
    {
        if (!_stateValidator.IsValid(state)) 
            throw new ArgumentException("Not a valid State");
        _locationRepository.Insert(city, state);
    }
}
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
public interface ILocationRepository
{
    void Insert(string city, string state);
}

public class LocationRepository : ILocationRepository
{
    public void Insert(string city, string state)
    {
        using (var con = new SqlConnection(Global.ConnectionString))
        using (var command = con.CreateCommand()) {
            command.CommandText = 
                @"Insert into Locations ([State], City)
                  Values(@State, @City)";

            command.Parameters.AddWithValue("State", state);
            command.Parameters.AddWithValue("City", city);  

            con.Open();
            command.ExecuteNonQuery();
        }
    }
}   
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
public interface IStateValidator
{
    bool IsValid(string state);
}

public class StateValidator : IStateValidator
{
    static readonly List<string> ValidStates = new List<string>
    {
        "AL", "AK", "AZ" //etc..
    };

    public bool IsValid(string state)
    {
        return ValidStates.Contains(state);
    }
}
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
public class ServiceModule : NinjectModule
{
    public override void Load()
    {
        Bind<ILocationRepository>().To<LocationRepository>();

        Bind<IStateValidator>().To<StateValidator>();
    }
}
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
public class LocationsManager  
{
    readonly ILocationRepository _locationRepository;
    readonly IStateValidator _stateValidator;

    public LocationsManager(ILocationRepository r, IStateValidator v)
    {
        _locationRepository = r; 
        _stateValidator = v;
    }

    public void CreateLocation(string city, string state)
    {
        if (!_stateValidator.IsValid(state)) 
            throw new ArgumentException("Not a valid State");
        _locationRepository.Insert(city, state);
    }
}

Code Complete by Steve McConnell

Industry Average: about 15 - 50 errors per 1000 lines of delivered code.


Microsoft Applications: about 10 - 20 defects per 1000 lines of code during in-house testing

Can we stay SOLID without code bloat?
If you have a class with two methods and one of them is the constructor you have a function

@JackDied

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
public class LocationsManager  
{
    readonly ILocationRepository _locationRepository;
    readonly IStateValidator _stateValidator;

    public LocationsManager(ILocationRepository r, IStateValidator v)
    {
        _locationRepository = r; 
        _stateValidator = v;
    }

    public void CreateLocation(string city, string state)
    {
        if (!_stateValidator.IsValid(state)) 
            throw new ArgumentException("Not a valid State");
        _locationRepository.Insert(city, state);
    }
}
1: 
2: 
3: 
4: 
let createLocation insert isValid city state =
    if state |> isValid
    then insert city state
    else raise (System.ArgumentException("Not a valid State"))
But then how do I use Dependency Injection and IOC containers?

Partial Application

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let add a b = a + b
add 5 2 
// 7

let addFive = add 5
addFive 2
// 7

In functional languages, all functions are interfaces

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
interface ICalculator { int Calculate(int input); }

class AddingCalculator: ICalculator
{
   public int Calculate(int input) { return input + 1; }
}

class LoggingCalculator: ICalculator
{
   ICalculator _innerCalculator;
   LoggingCalculator(ICalculator innerCalculator)
   {
      _innerCalculator = innerCalculator;
   }

   public int Calculate(int input) 
   { 
      Console.WriteLine("input is {0}", input);
      var result  = _innerCalculator.Calculate(input);
      Console.WriteLine("result is {0}", result);
      return result; 
   }
}

Equivalent* JavaScript

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
var addingCalculator = function(input) { return input + 1 }

var log = function(func, input) {
    console.log(input);
    var result = func(input);
    console.log(result);
    return result;
}

Statically checked types in C#

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public static TResult Log<T, TResult>(Func<T, TResult> func, T input)
{
    Console.WriteLine("input is " + input.ToString());
    var result = func(input);
    Console.WriteLine("result is " + result.ToString());
    return result;
}

The same code in F#

1: 
2: 
3: 
4: 
5: 
let log func input = 
    printfn "input is %A" input
    let result = func input
    printfn "result is %A" result
    result
Statically type check your data access
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
type LocationInsert = 
    SqlCommandProvider<
        "INSERT INTO Locations(State, City, [Date])
        VALUES (@State, @City, @Date)", "connectionString">

let insertLocation city state date =
    use command = new LocationInsert()
    command.Execute(state, city, date)

No more nulls

What is the result type of this function?

1: 
2: 
3: 
function divide(a, b) {
    return a / b;
}
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
type Option<'T> =
    | Some of 'T
    | None

let divideBy bottom top =
    if bottom = 0
    then None
    else Some(top/bottom)

8 |> divideBy 4;;
//int option = Some 2

8 |> divideBy 0;;
//int option = None
Staying focused on the happy path

Happy Path in C#

1: 
2: 
3: 
4: 
5: 
6: 
7: 
public string InsertLocation(Location location)
{
    validateRequest(location);
    db.updateDbFromRequest(location);
    email.EmailNearbyCustomers(location);
    return "Success";
}
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
public string InsertLocation(Location location)
{
    if (!validateRequest(location))
        throw new ArgumentException("Location not valid");
    db.updateDbFromRequest(location);
    email.EmailNearbyCustomers(location);
    return "Success";
}

Unhappy Path in C#

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
public string InsertLocation(Location location)
{
    if (!validateRequest(location))
        return "Failure";
    try
    {
        db.updateDbFromRequest(location);
    } 
    catch (Exception e)
    {
        Logger.Log(e);
        return "Failure";
    }   
    email.EmailNearbyCustomers(location);
    return "Success";
}

Happy Path in F#

1: 
2: 
3: 
4: 
let insertLocation = 
    validateRequest
    >>= updateDbFromRequest
    >>= emailNearbyCustomers

Unhappy Path in F#

1: 
2: 
3: 
4: 
let insertLocation = 
    validateRequest
    >>= updateDbFromRequest
    >>= emailNearbyCustomers
How is that possible?
1: 
2: 
3: 
4: 
5: 
$.when([1,2,3])
.then(function (data) {
    console.log(data)
});
// [1,2,3]
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
function log (x) { console.log(x) }
function addOne (x) { return x + 1 }

[lang=js]
$.when([1,2,3])
.then(function (data) {
    return data.map(addOne)
}).then(log);   
// [2,3,4]

C# .NET 4.5

1: 
2: 
3: 
4: 
5: 
6: 
async Task<int> AddSomeNumbers()
{
    var one = await Task.FromResult(1);
    var two = await Task.FromResult(2);
    return one + two;
}

Javascript ES7

1: 
2: 
3: 
4: 
async function getTweetContent() {
    let tweet = await getJSON('http://twitter.com/mytweet.json');
    return tweet.Content;
}
These are all examples of Monads
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
type Option<'T> =
    | Some of 'T
    | None

let divideBy bottom top =
    if bottom = 0
    then None
    else Some(top/bottom)

8 |> divideBy 4
//int option = Some 2

8 |> divideBy 0
//int option = None

Chaining Option<'T>

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
let divideByWorkflow init x y = maybe {
    let! a = init |> divideBy x
    let! b = a |> divideBy y
    return b
    }

divideByWorkflow 12 3 2
//int option = Some 2

divideByWorkflow 12 0 1
//int option = None

Defining Maybe

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
type MaybeBuilder() =
    member this.Return(x) = Some x
    member this.Bind(x, f) = 
        match x with
        | None -> None
        | Some a -> f a

let maybe = new MaybeBuilder()
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
let divideByWorkflow init x y = maybe {
    let! a = init |> divideBy x
    let! b = a |> divideBy y
    return b
    }

divideByWorkflow 12 3 2
//int option = Some 2

divideByWorkflow 12 0 1
//int option = None
Aren't Monads a Haskell thing?

Monad in F#

1: 
2: 
3: 
4: 
5: 
6: 
type MaybeBuilder() =
    member this.Return(x) = Some x
    member this.Bind(x, f) = 
        match x with
        | None -> None
        | Some a -> f a

Monad in Haskell

1: 
2: 
3: 
4: 
class Monad m where {
  (>>=)  :: m a -> (a -> m b) -> m b
  return :: a  -> m a
}   

Equivalent Haskell Monad in F#

1: 
2: 
3: 
4: 
5: 
type Monad<'M> =
    abstract member bind : 'M<'a> -> ('a -> 'M<'b>) -> 'M<'b>
    abstract member ``return`` : 'a -> 'M<'a>

//error FS0712: Type parameter cannot be used as type constructor
How do I choose a functional language?
  • Haskell - Pure language
  • Clojure - A Lisp on JVM
  • F# - Functional first on CLR / .NET
  • Erlang / Elixir - Massive scalability

Anecdotal Results of switching to FP

  • Social dealer management system for Freightliner
  • 5 months development
  • 3,582 lines of code
  • 2 bugs not caught at compile time

Bug #1

1: 
2: 
3: 
4: 
5: 
6: 
7: 
type GetChildObjects = 
    SqlCommandProvider<
        "Select * FROM ChildTable where id = @id", "connectionString">

type GetChildObjects' = 
    SqlCommandProvider<
        "Select * FROM ChildTable where parentId = @id", "connectionString">    

Bug #2

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
type GetChildObjects = 
    SqlCommandProvider<
        "Select * FROM ChildTable where parentId = @id", "connectionString">

let getChildObjects parentId = 
    use query = new GetChildObjects()
    query.Execute(parentId)
    |> Seq.map (fun x -> { Id = x.Id; Name = x.Name })

The model you use to view the world shapes the thoughts you are able to think.

@TheBurningMonk

@ReidNEvans

http://reidevans.tech

Links

http://fsharpforfunandprofit.com

Monads in pictures http://tinyurl.com/MonadsInPictures

Look, No Mocks! Functional TDD with F# http://www.infoq.com/presentations/mock-fsharp-tdd

http://theburningmonk.com/2015/04/dont-learn-a-syntax-learn-to-change-the-way-you-think/