Mixing functional and object oriented approaches to programming in C#

127
Mixing Functional and Object Oriented approaches to programming in C# Mike Wagg & Mark Needham

description

 

Transcript of Mixing functional and object oriented approaches to programming in C#

Page 1: Mixing functional and object oriented approaches to programming in C#

Mixing Functional and Object Oriented approaches to programming in C#

Mike Wagg & Mark Needham

Page 2: Mixing functional and object oriented approaches to programming in C#

C# 1.0

Page 3: Mixing functional and object oriented approaches to programming in C#

http://www.impawards.com/2003/posters/back_in_the_day.jpg

Page 4: Mixing functional and object oriented approaches to programming in C#

int[] ints = new int[] {1, 2, 3, 4, 5}

 

Page 5: Mixing functional and object oriented approaches to programming in C#

int[] Filter(int[] ints){    ArrayList results = new ArrayList();

    foreach (int i in ints)    {

if (i % 2 == 0)

{

results.Add(i);

}    }

    return results.ToArray(typeof(int));}

Page 6: Mixing functional and object oriented approaches to programming in C#

int[] ints = new int[] {1, 2, 3, 4, 5}

 

Page 7: Mixing functional and object oriented approaches to programming in C#

int[] Filter(int[] ints){    ArrayList results = new ArrayList();

    foreach (int i in ints)    {

if (i >3)

{

results.Add(i);

}    }

    return results.ToArray(typeof(int));}

Page 8: Mixing functional and object oriented approaches to programming in C#

int[] Filter(int[] ints){    ArrayList results = new ArrayList();

    foreach (int i in ints)    {

if (i % 2 == 0)

{

results.Add(i);

}    }

    return results.ToArray(typeof(int));}

Page 9: Mixing functional and object oriented approaches to programming in C#

int[] Filter(int[] ints){    ArrayList results = new ArrayList();

    foreach (int i in ints)    {

if (i > 3)

{

results.Add(i);

}    }

    return results.ToArray(typeof(int));}

Page 10: Mixing functional and object oriented approaches to programming in C#

interface IIntegerPredicate{

bool Matches(int value);}

Page 11: Mixing functional and object oriented approaches to programming in C#

class EvenPredicate : IIntegerPredicate{

bool Matches(int value)

{

return value % 2 == 0;

}}

Page 12: Mixing functional and object oriented approaches to programming in C#

class GreaterThanThreePredicate : IIntegerPredicate{

bool Matches(int value)

{

return value > 3;

}}

Page 13: Mixing functional and object oriented approaches to programming in C#

int[] Filter(int[] ints, IIntegerPredicate predicate){    ArrayList results = new ArrayList();

    foreach (int i in ints)    {

if (predicate.Matches(i))

{

results.Add(i);

}    }

    return results.ToArray(typeof(int));}

Page 14: Mixing functional and object oriented approaches to programming in C#

int[] ints = new int[] {1, 2, 3, 4, 5 };

int[] even = Filter(ints, new EvenPredicate());

int[] greaterThanThree = Filter(ints, new GreaterThanThreePredicate());

Page 15: Mixing functional and object oriented approaches to programming in C#

interface IIntegerPredicate{

bool Matches(int value);}

Page 16: Mixing functional and object oriented approaches to programming in C#

bool delegate IntegerPredicate(int value);

Page 17: Mixing functional and object oriented approaches to programming in C#

bool Even(int value){

return value % 2 == 0;}

Page 18: Mixing functional and object oriented approaches to programming in C#

bool GreaterThanThree(int value){

return value > 3;}

Page 19: Mixing functional and object oriented approaches to programming in C#

int[] Filter(int[] ints, IntegerPredicate predicate){    ArrayList results = new ArrayList();

    foreach (int i in ints)    {                

if (predicate(i))

{

results.Add(i);

}    }

    return results.ToArray(typeof(int));}

Page 20: Mixing functional and object oriented approaches to programming in C#

int[] ints = new int[] {1, 2, 3, 4, 5 };

int[] even = Filter(ints, 

new IntegerPredicate(Even));

Int[] greaterThanThree = Filter(ints, 

new IntegerPredicate(GreaterThanThree));

Page 21: Mixing functional and object oriented approaches to programming in C#

C# 2.0

 

Page 22: Mixing functional and object oriented approaches to programming in C#

Inference

int[] ints = new int[] {1, 2, 3, 4, 5 };

int[] even = Filter(ints, new IntegerPredicate(Even));

Int[] greaterThanThree = Filter(ints, new IntegerPredicate(GreaterThanThree));

Page 23: Mixing functional and object oriented approaches to programming in C#

Inference

int[] ints = new int[] {1, 2, 3, 4, 5 };

int[] even = Filter(ints, Even);

Int[] greaterThanThree = Filter(ints, GreaterThanThree);

The compiler can infer what the type of the delegate is so we don’thave to write it.

Page 24: Mixing functional and object oriented approaches to programming in C#

Generics

delegate bool IntegerPredicate(int value);

Page 25: Mixing functional and object oriented approaches to programming in C#

Generics

delegate bool Predicate<T> (T value);

Page 26: Mixing functional and object oriented approaches to programming in C#

Generics

int[] Filter(int[] ints, IntegerPredicate predicate){

ArrayList results = new ArrayList();

foreach (int i in ints){

if (predicate(i)){

results.Add(i);}

}

return results.ToArray(typeof(int));}

Page 27: Mixing functional and object oriented approaches to programming in C#

Generics

T[] Filter<T>(T[] values, Predicate<T> predicate){

List<T> results = new List<T>();

foreach (T i in value){

if (predicate(i)){

results.Add(i);}

}

return results.ToArray();}

Page 28: Mixing functional and object oriented approaches to programming in C#

Generics

IEnumerable<T> Filter<T>(IEnumerable<T> values, Predicate<T> predicate)

{List<T> results = new List<T>();

foreach (T i in value){

if (predicate(i)){

results.Add(i);}

}

return results;}

Page 29: Mixing functional and object oriented approaches to programming in C#

Iterators

IEnumerable<T> Filter<T>(IEnumerable<T> values, Predicate<T> predicate)

{List<T> results = new List<T>();

foreach (T i in value){

if (predicate(i)){

results.Add(i);}

}

return results;}

Page 30: Mixing functional and object oriented approaches to programming in C#

Iterators

IEnumerable<T> Filter<T>(IEnumerable<T> values, Predicate<T> predicate)

{foreach (T i in value){

if (predicate(i)){

yield return i;}

}}

Page 31: Mixing functional and object oriented approaches to programming in C#

Anonymous Methods

IEnumerable<int> greaterThanThree = Filter(ints, GreaterThanThree);

Page 32: Mixing functional and object oriented approaches to programming in C#

Anonymous Methods

IEnumerable<int> greaterThanThree = Filter(ints, delegate(int value) { return value > 3; });

Page 33: Mixing functional and object oriented approaches to programming in C#

Anonymous Methods

int minimumValue = 3;

IEnumerable<int> greaterThanThree = Filter(ints, delegate(int value) { return value > minimumValue; });

Anonymous methods add support for closures. The delegate captures the scope it was defined in.

Page 34: Mixing functional and object oriented approaches to programming in C#

C# 3.0

 

Page 35: Mixing functional and object oriented approaches to programming in C#

Lambdas

int minimumValue = 3;

IEnumerable<int> greaterThanThree = Filter(ints, delegate(int value) { return value > minimumValue; });

Page 36: Mixing functional and object oriented approaches to programming in C#

Lambdas

int minimumValue = 3;

IEnumerable<int> greaterThanThree = Filter(ints, value => value > minimumValue);

Page 37: Mixing functional and object oriented approaches to programming in C#

More Type Inference

int minimumValue = 3;

IEnumerable<int> greaterThanThree = Filter(ints, value => value > minimumValue);

Page 38: Mixing functional and object oriented approaches to programming in C#

More Type Inference

int minimumValue = 3;

var greaterThanThree = Filter(ints, value => value > minimumValue);

Page 39: Mixing functional and object oriented approaches to programming in C#

Extension Methods

int minimumValue = 3;

var greaterThanThree = Filter(ints, value => value > minimumValue);

Page 40: Mixing functional and object oriented approaches to programming in C#

Extension Methods

int minimumValue = 3;

var greaterThanThree = ints.Filter(value => value > minimumValue);

Page 41: Mixing functional and object oriented approaches to programming in C#

Anonymous Types

var anonymous = new{

Foo = 1,Bar = “Bar”

}

Page 42: Mixing functional and object oriented approaches to programming in C#

LINQ

Page 43: Mixing functional and object oriented approaches to programming in C#

LINQ

New delegates in System namespaceAction<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc.

Page 44: Mixing functional and object oriented approaches to programming in C#

LINQ

New delegates in System namespaceAction<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc.

System.LinqExtension methods Where, Select, OrderBy etc.

Page 45: Mixing functional and object oriented approaches to programming in C#

LINQ

New delegates in System namespaceAction<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc.

System.LinqExtension methods Where, Select, OrderBy etc. Some compiler magic to translate sql style code to method calls

Page 46: Mixing functional and object oriented approaches to programming in C#

LINQ

var even = ints.Where(value => value % 2 == 0) var greaterThanThree = ints.Where(value => value > minimumValue) or

var even = from value in ints                  where value % 2 == 0                  select value  var greaterThanThree = from value in ints                                       where value > minimumValue                                                   select value                                      

Page 47: Mixing functional and object oriented approaches to programming in C#

A (little) bit of theory

Page 48: Mixing functional and object oriented approaches to programming in C#

http://www.flickr.com/photos/stuartpilbrow/2938100285/sizes/l/

Page 49: Mixing functional and object oriented approaches to programming in C#

Higher order functions

Page 50: Mixing functional and object oriented approaches to programming in C#

Immutability

Page 51: Mixing functional and object oriented approaches to programming in C#

Lazy evaluation

Page 52: Mixing functional and object oriented approaches to programming in C#

Recursion & Pattern Matching

Page 53: Mixing functional and object oriented approaches to programming in C#

Transformational MindsetWe can just pass functions around instead in most cases- find an example where it still makes sense to use the GOF approach though.

Page 54: Mixing functional and object oriented approaches to programming in C#

Input -> ??? -> ??? -> ??? -> Output

Page 55: Mixing functional and object oriented approaches to programming in C#

http://www.emt-india.net/process/petrochemical/img/pp4.jpg

Page 56: Mixing functional and object oriented approaches to programming in C#

So why should you care?

Page 57: Mixing functional and object oriented approaches to programming in C#

Functional can fill in the gaps in OO code

 

Page 58: Mixing functional and object oriented approaches to programming in C#

Abstractions over common operations means less code and less chances to make

mistakes

Page 59: Mixing functional and object oriented approaches to programming in C#

So what do we get out of the box?

Page 60: Mixing functional and object oriented approaches to programming in C#

Projection

Page 61: Mixing functional and object oriented approaches to programming in C#

 

  

 people.Select(person => person.Name)  

Page 62: Mixing functional and object oriented approaches to programming in C#

 

  

people.SelectMany(person => person.Pets)

Page 63: Mixing functional and object oriented approaches to programming in C#

Restriction

 

Page 64: Mixing functional and object oriented approaches to programming in C#

 

         

people.Where(person => person.HasPets) 

Page 65: Mixing functional and object oriented approaches to programming in C#

Partitioning

 

Page 66: Mixing functional and object oriented approaches to programming in C#

 

  

people.Take(5)

Page 67: Mixing functional and object oriented approaches to programming in C#

 

  

people.Skip(5)

Page 68: Mixing functional and object oriented approaches to programming in C#

 

  

            people.TakeWhile(person =>                             person.Name != "David")

Page 69: Mixing functional and object oriented approaches to programming in C#

 

              people.SkipWhile(person =>                                 person.Name != "David")

Page 70: Mixing functional and object oriented approaches to programming in C#

Set

 

Page 71: Mixing functional and object oriented approaches to programming in C#

 

people.Select(person => person.Name).Distinct()

Page 72: Mixing functional and object oriented approaches to programming in C#

 

 people.Union(someOtherPeople) 

Page 73: Mixing functional and object oriented approaches to programming in C#

 

  

people.Intersect(someOtherPeople)

Page 74: Mixing functional and object oriented approaches to programming in C#

 

  

people.Except(someOtherPeople)

Page 75: Mixing functional and object oriented approaches to programming in C#

Ordering and Grouping

 

Page 76: Mixing functional and object oriented approaches to programming in C#

 

  

people.OrderBy(person => person.Name)  

Page 77: Mixing functional and object oriented approaches to programming in C#

 

  

people.GroupBy(person => person.Name)

Page 78: Mixing functional and object oriented approaches to programming in C#

Aggregation

 

Page 79: Mixing functional and object oriented approaches to programming in C#

 

  

people.Count()

Page 80: Mixing functional and object oriented approaches to programming in C#

 

  

people.Select(person => person.Age).Sum()

Page 81: Mixing functional and object oriented approaches to programming in C#

 

  

people.Select(person => person.Age).Min()

Page 82: Mixing functional and object oriented approaches to programming in C#

 

  

people.Select(person => person.Age).Max()

Page 83: Mixing functional and object oriented approaches to programming in C#

 

  

people.Select(person => person.Age).Average()

Page 84: Mixing functional and object oriented approaches to programming in C#

 

Things can get more complex

Page 85: Mixing functional and object oriented approaches to programming in C#

 

  

       people.Select(person => person.Age)            .Aggregate(0, (totalAge, nextAge) =>                     nextAge % 2 == 0                         ? nextAge + totalAge                         : totalAge)

Page 86: Mixing functional and object oriented approaches to programming in C#

 

          people.Join(addresses,                           person => person.PersonId,                  address => address.PersonId,                     (person, address) => new {                                         person, address})

Page 87: Mixing functional and object oriented approaches to programming in C#

We can just pass functions around instead in most cases- find an example where it still makes sense to use the GOF approach though.

Page 88: Mixing functional and object oriented approaches to programming in C#
Page 89: Mixing functional and object oriented approaches to programming in C#

 

 

public class SomeObject{

private readonly IStrategy strategy;

public SomeObject(IStrategy strategy){

this.strategy = strategy;}

public void DoSomething(string value){

strategy.DoSomething(value);}

}

Page 90: Mixing functional and object oriented approaches to programming in C#

 

 public class Strategy : IStrategy{

public void DoSomething(string value){

// do something with string}

}

Page 91: Mixing functional and object oriented approaches to programming in C#

 

 

public class SomeObject{

private readonly Action<string> strategy;

public SomeObject(Action<string> strategy){

this.strategy = strategy;}

public void DoSomething(string value){

strategy(value);}

}

Page 92: Mixing functional and object oriented approaches to programming in C#

 

Hole in the middle pattern

Page 93: Mixing functional and object oriented approaches to programming in C#

 

 

public class ServiceCache<Service>{

protected Res FromCacheOrService          <Req, Res>(Func<Res> serviceCall, Req request)

{     var cachedRes = cache.RetrieveIfExists(

typeof(Service), typeof(Res), request);

  if(cachedRes == null)  {

cachedRes = serviceCall();cache.Add(typeof(Service), request,

cachedRes);}

     return (Res) cachedRes;}

}

Page 94: Mixing functional and object oriented approaches to programming in C#

 

 public class CachedService : ServiceCache<IService>{

public MyResult GetMyResult(MyRequest request){

          return FromCacheOrService(()                  => service.GetMyResult(request), request);

}}

Page 95: Mixing functional and object oriented approaches to programming in C#

 

Passing functions around

Page 96: Mixing functional and object oriented approaches to programming in C#

 

private void AddErrorIf<T>(Expression<Func<T>> fn, ModelStateDictionary modelState,

Func<ModelStateDictionary, Func<T,string, string, bool>> checkFn)

{var fieldName = ((MemberExpression)fn.Body).Member.Name;var value = fn.Compile().Invoke();var validationMessage = validationMessages[fieldName]);

checkFn.Invoke(modelState)(value, fieldName, validationMessage);}

AddErrorIf(() => person.HasPets, modelState, m => (value, field, error) => m.AddErrorIfNotEqualTo(value,true, field,

error));

AddErrorIf(() => person.HasChildren, modelState, m => (value, field, error) => m.AddErrorIfNull(value, field, error));

Page 97: Mixing functional and object oriented approaches to programming in C#

 

Continuation Passing Style

Page 98: Mixing functional and object oriented approaches to programming in C#

 

static void Identity<T>(T value, Action<T> k) {

k(value); }

Page 99: Mixing functional and object oriented approaches to programming in C#

 

Identity("foo", s => Console.WriteLine(s));

Page 100: Mixing functional and object oriented approaches to programming in C#

 

Identity("foo", s => Console.WriteLine(s));

as compared to

var foo = Identity(“foo”);Console.WriteLine(foo);

Page 101: Mixing functional and object oriented approaches to programming in C#

 

public ActionResult Submit(string id, FormCollection form) {    var shoppingBasket = CreateShoppingBasketFrom(id, form);      return IsValid(shoppingBasket, ModelState,          () => RedirectToAction("index", "ShoppingBasket", new { shoppingBasket.Id} ),   () => LoginUser(shoppingBasket,                  () =>                  {                    ModelState.AddModelError("Password", "User name/email address was incorrect - please re-enter");                      return RedirectToAction("index", ""ShoppingBasket",

new { Id = new Guid(id) });                 }, user =>                  {                    shoppingBasket.User = user;                      UpdateShoppingBasket(shoppingBasket);                      return RedirectToAction("index", "Purchase",   

new { Id = shoppingBasket.Id }); }         )); }

Page 102: Mixing functional and object oriented approaches to programming in C#

 

private RedirectToRouteResult IsValid(ShoppingBasket shoppingBasket,                                       ModelStateDictionary modelState,                                        Func<RedirectToRouteResult> failureFn,                     

Func<RedirectToRouteResult> successFn) {   return validator.IsValid(shoppingBasket, modelState) ? successFn() : failureFn(); }  

private RedirectToRouteResult LoginUser(ShoppingBasket shoppingBasket,                                                                  Func<RedirectToRouteResult> failureFn,                                                                Func<User,RedirectToRouteResult> successFn) {

User user = null; try {

user = userService.CreateAccountOrLogIn(shoppingBasket); }   catch (NoAccountException) {

return failureFn(); }   return successFn(user);

}

Page 103: Mixing functional and object oriented approaches to programming in C#

 

http://www.thegeekshowpodcast.com/home/mastashake/thegeekshowpodcast.com/wp-content/uploads/2009/07/wtf-cat.jpg

Page 104: Mixing functional and object oriented approaches to programming in C#

So what could possibly go wrong?

 

http://icanhascheezburger.files.wordpress.com/2009/06/funny-pictures-cat-does-not-think-plan-will-fail.jpg

Page 105: Mixing functional and object oriented approaches to programming in C#

Hard to diagnose errors

 

Page 106: Mixing functional and object oriented approaches to programming in C#

var people = new [] {

new Person { Id=1, Address = new Address { Road = "Ewloe Road" }}, new Person { Id=2},

new Person { Id=3, Address = new Address { Road = "London Road"}} };

people.Select(p => p.Address.Road); 

Page 107: Mixing functional and object oriented approaches to programming in C#

Null Reference Exception on line 23

Page 108: Mixing functional and object oriented approaches to programming in C#

http://www.flickr.com/photos/29599641@N04/3147972713/

Page 109: Mixing functional and object oriented approaches to programming in C#

public T Tap(T t, Action action) {    action();    return t;}

Page 110: Mixing functional and object oriented approaches to programming in C#

people    .Select(p => Tap(p, logger.debug(p.Id))    .Select(p => p.Address.Road); 

Page 111: Mixing functional and object oriented approaches to programming in C#

Readability

 

Page 112: Mixing functional and object oriented approaches to programming in C#

Lazy evaluation can have unexpected consequences

 

Page 113: Mixing functional and object oriented approaches to programming in C#

         IEnumerable<string> ReadNamesFromFile()        {            using(var fileStream = new FileStream("names.txt",                                                         FileMode.Open))            using(var reader = new StreamReader(fileStream))            {                var nextLine = reader.ReadLine();                while(nextLine != null)                {                    yield return nextLine;                    nextLine = reader.ReadLine();                }            }        }

Page 114: Mixing functional and object oriented approaches to programming in C#

 

        IEnumerable<Person> GetPeople()        {            return ReadNamesFromFile()                    .Select(name => new Person(name));        }

Page 115: Mixing functional and object oriented approaches to programming in C#

 

     IEnumerable<Person> people = GetPeople();         foreach (var person in people)     {         Console.WriteLine(person.Name);     }         Console.WriteLine("Total number of people: " +                                     people.Count());

Page 116: Mixing functional and object oriented approaches to programming in C#

Encapsulation is still important

 

Page 117: Mixing functional and object oriented approaches to programming in C#

 

Total salary for a company company.Employees.Select(employee =>                                             employee.Salary)                                  .Sum()

This could lead to duplication

What if we add rules to the calculation?

Who should really have this responsibility?.Sum()

Page 118: Mixing functional and object oriented approaches to programming in C#

Linq isn't the problem here, it's where we have put it

 

Page 119: Mixing functional and object oriented approaches to programming in C#

Company naturally has the responsibility so encapsulate

the logic here

Page 120: Mixing functional and object oriented approaches to programming in C#

class Company{    public int TotalSalary    {        get         {            return employees.Select(e =>                                     e.Salary).Sum();         }     } }

Page 121: Mixing functional and object oriented approaches to programming in C#

Sometimes we need to go further

 

Page 122: Mixing functional and object oriented approaches to programming in C#

If both Company and Division have employees do we

duplicate the logic for total salary?

Page 123: Mixing functional and object oriented approaches to programming in C#

IEnumerable<T> and List<T> make collections easy but

sometimes it is still better to create a class to represent a

collection

Page 124: Mixing functional and object oriented approaches to programming in C#

 

class EmployeeCollection{    private List<Employee> employees;       public int TotalSalary    {        get        {            return employees.Select(e => e.Salary).Sum();         }    }}

Page 125: Mixing functional and object oriented approaches to programming in C#

In conclusion…

 

Page 126: Mixing functional and object oriented approaches to programming in C#