SlideShare a Scribd company logo
Mixing Functional and Object Oriented approaches to programming in C# Mike Wagg & Mark Needham
C# 1.0
http://www.impawards.com/2003/posters/back_in_the_day.jpg
int[] ints = new int[] {1, 2, 3, 4, 5}  
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)); }
int[] ints = new int[] {1, 2, 3, 4, 5}  
int[] Filter(int[] ints) {     ArrayList results = new ArrayList();     foreach (int i in ints)     { if (i >3) { results.Add(i); }     }     return results.ToArray(typeof(int)); }
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)); }
int[] Filter(int[] ints) {     ArrayList results = new ArrayList();     foreach (int i in ints)     { if ( i > 3 ) { results.Add(i); }     }     return results.ToArray(typeof(int)); }
interface IIntegerPredicate { bool Matches(int value); }
class EvenPredicate : IIntegerPredicate { bool Matches(int value) { return value % 2 == 0; } }
class GreaterThanThreePredicate : IIntegerPredicate { bool Matches(int value) { return value > 3; } }
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)); }
int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints, new EvenPredicate()); int[] greaterThanThree = Filter(ints, new  GreaterThanThreePredicate());
interface IIntegerPredicate { bool Matches(int value); }
bool delegate IntegerPredicate(int value);
bool Even(int value) { return value % 2 == 0; }
bool GreaterThanThree(int value) { return value > 3; }
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)); }
int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints,  new IntegerPredicate(Even)); Int[] greaterThanThree = Filter(ints,  new IntegerPredicate(GreaterThanThree));
C# 2.0  
Inference int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints,  new IntegerPredicate(Even)); Int[] greaterThanThree = Filter(ints,  new IntegerPredicate(GreaterThanThree));
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’t have to write it.
Generics delegate bool IntegerPredicate(int value);
Generics delegate bool Predicate<T> (T value);
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)); }
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(); }
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; }
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; }
Iterators IEnumerable<T> Filter<T>(IEnumerable<T> values,  Predicate<T> predicate) { foreach (T i in value) { if (predicate(i)) { yield return i; } } }
Anonymous Methods IEnumerable<int> greaterThanThree = Filter(ints, GreaterThanThree);
Anonymous Methods IEnumerable<int> greaterThanThree = Filter(ints,  delegate(int value) { return value > 3; });
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.
C# 3.0  
Lambdas int minimumValue = 3; IEnumerable<int> greaterThanThree = Filter(ints,  delegate(int value) { return value > minimumValue; });
Lambdas int minimumValue = 3; IEnumerable<int> greaterThanThree = Filter(ints,  value => value > minimumValue);
More Type Inference int minimumValue = 3; IEnumerable<int> greaterThanThree = Filter(ints,  value => value > minimumValue);
More Type Inference int minimumValue = 3; var greaterThanThree = Filter(ints, value => value > minimumValue);
Extension Methods int minimumValue = 3; var greaterThanThree = Filter(ints, value => value > minimumValue);
Extension Methods int minimumValue = 3; var greaterThanThree = ints.Filter(value => value > minimumValue);
Anonymous Types var anonymous = new { Foo = 1, Bar = “Bar” }
LINQ
LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc.
LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc. System.Linq Extension methods Where, Select, OrderBy etc.
LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc. System.Linq Extension methods Where, Select, OrderBy etc.   Some compiler magic to translate sql style code to method calls
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                                       
A (little) bit of theory
http://www.flickr.com/photos/stuartpilbrow/2938100285/sizes/l/
Higher order functions
Immutability
Lazy evaluation
Recursion & Pattern Matching
Transformational Mindset We can just pass functions around instead in most cases - find an example where it still makes sense to use the GOF approach though.
Input  -> ??? -> ??? -> ??? ->  Output
http://www.emt-india.net/process/petrochemical/img/pp4.jpg
So why should you care?
Functional can fill in the gaps in OO code  
Abstractions over common operations means less code and less chances to make mistakes
So what do we get out of the box?
Projection
        people.Select(person => person.Name)    
      people.SelectMany(person => person.Pets)
Restriction  
             people.Where(person => person.HasPets)  
Partitioning  
      people.Take(5)
      people.Skip(5)
                   people.TakeWhile(person =>                               person.Name != &quot;David&quot;)
                   people.SkipWhile(person =>                                   person.Name != &quot;David&quot;)
Set  
  people.Select(person => person.Name) .Distinct()
    people.Union(someOtherPeople)  
      people.Intersect(someOtherPeople)
      people.Except(someOtherPeople)
Ordering and Grouping  
      people.OrderBy(person => person.Name)    
      people.GroupBy(person => person.Name)
Aggregation  
      people.Count()
      people.Select(person => person.Age) .Sum()
      people.Select(person => person.Age) .Min()
      people.Select(person => person.Age) .Max()
      people.Select(person => person.Age) .Average()
  Things can get more complex
              people.Select(person => person.Age)              .Aggregate(0, (totalAge, nextAge) =>                       nextAge % 2 == 0                           ? nextAge + totalAge                           : totalAge)
               people.Join(addresses,                             person => person.PersonId,                    address => address.PersonId,                       (person, address) => new {                                           person, address})
We can just pass functions around instead in most cases - find an example where it still makes sense to use the GOF approach though.
 
    public class SomeObject {  private readonly IStrategy strategy; public SomeObject(IStrategy strategy) { this.strategy = strategy; } public void DoSomething(string value) { strategy.DoSomething(value); } }
    public class Strategy : IStrategy {  public void DoSomething(string value) { // do something with string } }
    public class SomeObject {  private readonly Action<string> strategy; public SomeObject(Action<string> strategy) { this.strategy = strategy; } public void DoSomething(string value) { strategy(value); } }
  Hole in the middle pattern
    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; } }
    public class CachedService : ServiceCache<IService> {  public MyResult GetMyResult(MyRequest request) {            return FromCacheOrService(()                   => service.GetMyResult(request), request); } }
  Passing functions around
  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));
  Continuation Passing Style
  static void Identity<T>(T value, Action<T> k)  {  k(value);  }
  Identity(&quot;foo&quot;, s => Console.WriteLine(s));
  Identity(&quot;foo&quot;, s => Console.WriteLine(s));  as compared to var foo = Identity(“foo”); Console.WriteLine(foo);
  public ActionResult Submit(string id, FormCollection form) {     var shoppingBasket = CreateShoppingBasketFrom(id, form);      return IsValid(shoppingBasket, ModelState,          () => RedirectToAction(&quot;index&quot;, &quot;ShoppingBasket&quot;, new { shoppingBasket.Id} ),      () => LoginUser(shoppingBasket,                  () =>                    {                     ModelState.AddModelError(&quot;Password&quot;, &quot;User name/email address was      incorrect - please re-enter&quot;);                      return RedirectToAction(&quot;index&quot;, &quot;&quot;ShoppingBasket&quot;,    new { Id = new Guid(id) });                   },  user =>                    {                     shoppingBasket.User = user;                      UpdateShoppingBasket(shoppingBasket);                      return RedirectToAction(&quot;index&quot;, &quot;Purchase&quot;,      new { Id = shoppingBasket.Id });  }         ));  }
  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);  }
  http://www.thegeekshowpodcast.com/home/mastashake/thegeekshowpodcast.com/wp-content/uploads/2009/07/wtf-cat.jpg
So what could possibly go wrong?   http://icanhascheezburger.files.wordpress.com/2009/06/funny-pictures-cat-does-not-think-plan-will-fail.jpg
Hard to diagnose errors  
var people = new []  {  new Person { Id=1, Address = new Address { Road = &quot;Ewloe Road&quot; }},   new Person { Id=2}, new Person { Id=3, Address = new Address { Road = &quot;London Road&quot;}}  }; people.Select(p => p.Address.Road); 
Null Reference Exception on line 23
http://www.flickr.com/photos/29599641@N04/3147972713/
public T Tap(T t, Action action)  {     action();     return t; }
people     .Select(p => Tap(p, logger.debug(p.Id))     .Select(p => p.Address.Road); 
Readability  
Lazy evaluation can have unexpected consequences  
           IEnumerable<string> ReadNamesFromFile()         {             using(var fileStream = new FileStream(&quot;names.txt&quot;,                                                           FileMode.Open))             using(var reader = new StreamReader(fileStream))             {                 var nextLine = reader.ReadLine();                 while(nextLine != null)                 {                     yield return nextLine;                     nextLine = reader.ReadLine();                 }             }         }
           IEnumerable<Person> GetPeople()         {             return ReadNamesFromFile()                      .Select(name => new Person(name));         }
        IEnumerable<Person> people = GetPeople();           foreach (var person in people)      {          Console.WriteLine(person.Name);      }            Console.WriteLine(&quot;Total number of people: &quot; +                                       people.Count());
Encapsulation is still important  
  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()
Linq isn't the problem here, it's where we have put it  
Company naturally has the responsibility so encapsulate the logic here
class Company {      public int TotalSalary      {          get           {              return employees.Select(e =>                                       e.Salary).Sum();           }       }  }
Sometimes we need to go further  
If both Company and Division have employees do we duplicate the logic for total salary?
IEnumerable<T> and List<T> make collections easy but sometimes it is still better to create a class to represent a collection
  class EmployeeCollection {      private List<Employee> employees;          public int TotalSalary      {          get          {              return employees.Select(e => e.Salary).Sum();           }      } }
In conclusion…  
 
Mike Wagg mikewagg.blogspot.com [email_address] Mark Needham markhneedham.com [email_address]

More Related Content

What's hot (20)

PDF
Groovy
congcong wang
 
PPTX
Pj01 4-operators and control flow
SasidharaRaoMarrapu
 
PPT
Arrays searching-sorting
Ajharul Abedeen
 
PPT
Mixing functional and object oriented approaches to programming in C#
Mark Needham
 
PPTX
Linq Sanjay Vyas
rsnarayanan
 
PDF
Introduction to Erlang
Gabriele Lana
 
PPT
07slide
Dorothea Chaffin
 
PPT
Sorting
Abhishek Khune
 
PDF
awesome groovy
Paul King
 
PPT
Chapter 2 Method in Java OOP
Khirulnizam Abd Rahman
 
PDF
functional groovy
Paul King
 
PPTX
Refactoring
Bruno Quintella
 
PPT
Introduction To Groovy
manishkp84
 
PPT
Chapter 4 - Classes in Java
Khirulnizam Abd Rahman
 
PPT
Chapter 3 Arrays in Java
Khirulnizam Abd Rahman
 
PPTX
Top 20 java programming interview questions for sdet
DevLabs Alliance
 
PDF
Python dictionary : past, present, future
delimitry
 
PDF
MCE^3 - Hannes Verlinde - Let The Symbols Do The Work
PROIDEA
 
PDF
Python lecture 05
Tanwir Zaman
 
PPT
Oop lecture7
Shahriar Robbani
 
Groovy
congcong wang
 
Pj01 4-operators and control flow
SasidharaRaoMarrapu
 
Arrays searching-sorting
Ajharul Abedeen
 
Mixing functional and object oriented approaches to programming in C#
Mark Needham
 
Linq Sanjay Vyas
rsnarayanan
 
Introduction to Erlang
Gabriele Lana
 
07slide
Dorothea Chaffin
 
Sorting
Abhishek Khune
 
awesome groovy
Paul King
 
Chapter 2 Method in Java OOP
Khirulnizam Abd Rahman
 
functional groovy
Paul King
 
Refactoring
Bruno Quintella
 
Introduction To Groovy
manishkp84
 
Chapter 4 - Classes in Java
Khirulnizam Abd Rahman
 
Chapter 3 Arrays in Java
Khirulnizam Abd Rahman
 
Top 20 java programming interview questions for sdet
DevLabs Alliance
 
Python dictionary : past, present, future
delimitry
 
MCE^3 - Hannes Verlinde - Let The Symbols Do The Work
PROIDEA
 
Python lecture 05
Tanwir Zaman
 
Oop lecture7
Shahriar Robbani
 

Similar to Mixing Functional and Object Oriented Approaches to Programming in C# (20)

PPT
Whats new in_csharp4
Abed Bukhari
 
PPTX
Monadic Comprehensions and Functional Composition with Query Expressions
Chris Eargle
 
PPT
Lo Mejor Del Pdc2008 El Futrode C#
Juan Pablo
 
PPS
Let Us Learn Lambda Using C# 3.0
Sheik Uduman Ali
 
PPTX
Linq
Pranay Rana
 
PDF
.net code: some ideas to improve yours
Carlos Lopes
 
PPTX
Generic Programming &amp; Collection
Arya
 
PPTX
Generic Programming &amp; Collection
Arya
 
PPT
C# programming
umesh patil
 
PPT
The secret of Functional Programming revealed!
Torbjørn Marø
 
PPTX
Lambda expressions
Yuriy Seniuk
 
PPT
Whats New In C# 3.0
Clint Edmonson
 
PDF
1204csharp
g_hemanth17
 
PPT
N depend & cql
Roman Kalita
 
PPTX
Back-2-Basics: .NET Coding Standards For The Real World (2011)
David McCarter
 
PPT
PostThis
testingphase
 
PPT
C sharp part2
anjelo0028
 
PPT
devLink - What's New in C# 4?
Kevin Pilch
 
PPTX
Linq - an overview
neontapir
 
PPTX
Linq Introduction
Neeraj Kaushik
 
Whats new in_csharp4
Abed Bukhari
 
Monadic Comprehensions and Functional Composition with Query Expressions
Chris Eargle
 
Lo Mejor Del Pdc2008 El Futrode C#
Juan Pablo
 
Let Us Learn Lambda Using C# 3.0
Sheik Uduman Ali
 
Linq
Pranay Rana
 
.net code: some ideas to improve yours
Carlos Lopes
 
Generic Programming &amp; Collection
Arya
 
Generic Programming &amp; Collection
Arya
 
C# programming
umesh patil
 
The secret of Functional Programming revealed!
Torbjørn Marø
 
Lambda expressions
Yuriy Seniuk
 
Whats New In C# 3.0
Clint Edmonson
 
1204csharp
g_hemanth17
 
N depend & cql
Roman Kalita
 
Back-2-Basics: .NET Coding Standards For The Real World (2011)
David McCarter
 
PostThis
testingphase
 
C sharp part2
anjelo0028
 
devLink - What's New in C# 4?
Kevin Pilch
 
Linq - an overview
neontapir
 
Linq Introduction
Neeraj Kaushik
 
Ad

More from Skills Matter (20)

PDF
5 things cucumber is bad at by Richard Lawrence
Skills Matter
 
ODP
Patterns for slick database applications
Skills Matter
 
PDF
Scala e xchange 2013 haoyi li on metascala a tiny diy jvm
Skills Matter
 
ODP
Oscar reiken jr on our success at manheim
Skills Matter
 
ODP
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
Skills Matter
 
PDF
Cukeup nyc ian dees on elixir, erlang, and cucumberl
Skills Matter
 
PDF
Cukeup nyc peter bell on getting started with cucumber.js
Skills Matter
 
PDF
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
Skills Matter
 
ODP
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
Skills Matter
 
ODP
Progressive f# tutorials nyc don syme on keynote f# in the open source world
Skills Matter
 
PDF
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
Skills Matter
 
PPTX
Dmitry mozorov on code quotations code as-data for f#
Skills Matter
 
PDF
A poet's guide_to_acceptance_testing
Skills Matter
 
PDF
Russ miles-cloudfoundry-deep-dive
Skills Matter
 
KEY
Serendipity-neo4j
Skills Matter
 
PDF
Simon Peyton Jones: Managing parallelism
Skills Matter
 
PDF
Plug 20110217
Skills Matter
 
PDF
Lug presentation
Skills Matter
 
PPT
I went to_a_communications_workshop_and_they_t
Skills Matter
 
PDF
Plug saiku
Skills Matter
 
5 things cucumber is bad at by Richard Lawrence
Skills Matter
 
Patterns for slick database applications
Skills Matter
 
Scala e xchange 2013 haoyi li on metascala a tiny diy jvm
Skills Matter
 
Oscar reiken jr on our success at manheim
Skills Matter
 
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
Skills Matter
 
Cukeup nyc ian dees on elixir, erlang, and cucumberl
Skills Matter
 
Cukeup nyc peter bell on getting started with cucumber.js
Skills Matter
 
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
Skills Matter
 
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
Skills Matter
 
Progressive f# tutorials nyc don syme on keynote f# in the open source world
Skills Matter
 
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
Skills Matter
 
Dmitry mozorov on code quotations code as-data for f#
Skills Matter
 
A poet's guide_to_acceptance_testing
Skills Matter
 
Russ miles-cloudfoundry-deep-dive
Skills Matter
 
Serendipity-neo4j
Skills Matter
 
Simon Peyton Jones: Managing parallelism
Skills Matter
 
Plug 20110217
Skills Matter
 
Lug presentation
Skills Matter
 
I went to_a_communications_workshop_and_they_t
Skills Matter
 
Plug saiku
Skills Matter
 
Ad

Recently uploaded (20)

PPTX
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
PDF
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
PDF
HR agent at Mediq: Lessons learned on Agent Builder & Maestro by Tacstone Tec...
UiPathCommunity
 
PDF
Market Insight : ETH Dominance Returns
CIFDAQ
 
PDF
Bitcoin+ Escalando sin concesiones - Parte 1
Fernando Paredes GarcĂ­a
 
PDF
The Past, Present & Future of Kenya's Digital Transformation
Moses Kemibaro
 
PDF
2025-07-15 EMEA Volledig Inzicht Dutch Webinar
ThousandEyes
 
PDF
visibel.ai Company Profile – Real-Time AI Solution for CCTV
visibelaiproject
 
PDF
Alpha Altcoin Setup : TIA - 19th July 2025
CIFDAQ
 
PDF
Integrating IIoT with SCADA in Oil & Gas A Technical Perspective.pdf
Rejig Digital
 
PDF
Lecture A - AI Workflows for Banking.pdf
Dr. LAM Yat-fai (林日辉)
 
PDF
TrustArc Webinar - Navigating Data Privacy in LATAM: Laws, Trends, and Compli...
TrustArc
 
PDF
Rethinking Security Operations - Modern SOC.pdf
Haris Chughtai
 
PPTX
Machine Learning Benefits Across Industries
SynapseIndia
 
PDF
RAT Builders - How to Catch Them All [DeepSec 2024]
malmoeb
 
PDF
Upskill to Agentic Automation 2025 - Kickoff Meeting
DianaGray10
 
PDF
Novus Safe Lite- What is Novus Safe Lite.pdf
Novus Hi-Tech
 
PDF
OpenInfra ID 2025 - Are Containers Dying? Rethinking Isolation with MicroVMs.pdf
Muhammad Yuga Nugraha
 
PDF
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
PDF
Agentic Artificial Intelligence (AI) and its growing impact on business opera...
Alakmalak Technologies Pvt. Ltd.
 
Agile Chennai 18-19 July 2025 Ideathon | AI Powered Microfinance Literacy Gui...
AgileNetwork
 
Meetup Kickoff & Welcome - Rohit Yadav, CSIUG Chairman
ShapeBlue
 
HR agent at Mediq: Lessons learned on Agent Builder & Maestro by Tacstone Tec...
UiPathCommunity
 
Market Insight : ETH Dominance Returns
CIFDAQ
 
Bitcoin+ Escalando sin concesiones - Parte 1
Fernando Paredes GarcĂ­a
 
The Past, Present & Future of Kenya's Digital Transformation
Moses Kemibaro
 
2025-07-15 EMEA Volledig Inzicht Dutch Webinar
ThousandEyes
 
visibel.ai Company Profile – Real-Time AI Solution for CCTV
visibelaiproject
 
Alpha Altcoin Setup : TIA - 19th July 2025
CIFDAQ
 
Integrating IIoT with SCADA in Oil & Gas A Technical Perspective.pdf
Rejig Digital
 
Lecture A - AI Workflows for Banking.pdf
Dr. LAM Yat-fai (林日辉)
 
TrustArc Webinar - Navigating Data Privacy in LATAM: Laws, Trends, and Compli...
TrustArc
 
Rethinking Security Operations - Modern SOC.pdf
Haris Chughtai
 
Machine Learning Benefits Across Industries
SynapseIndia
 
RAT Builders - How to Catch Them All [DeepSec 2024]
malmoeb
 
Upskill to Agentic Automation 2025 - Kickoff Meeting
DianaGray10
 
Novus Safe Lite- What is Novus Safe Lite.pdf
Novus Hi-Tech
 
OpenInfra ID 2025 - Are Containers Dying? Rethinking Isolation with MicroVMs.pdf
Muhammad Yuga Nugraha
 
GDG Cloud Munich - Intro - Luiz Carneiro - #BuildWithAI - July - Abdel.pdf
Luiz Carneiro
 
Agentic Artificial Intelligence (AI) and its growing impact on business opera...
Alakmalak Technologies Pvt. Ltd.
 

Mixing Functional and Object Oriented Approaches to Programming in C#

  • 1. Mixing Functional and Object Oriented approaches to programming in C# Mike Wagg & Mark Needham
  • 4. int[] ints = new int[] {1, 2, 3, 4, 5}  
  • 5. 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)); }
  • 6. int[] ints = new int[] {1, 2, 3, 4, 5}  
  • 7. int[] Filter(int[] ints) {     ArrayList results = new ArrayList();     foreach (int i in ints)     { if (i >3) { results.Add(i); }     }     return results.ToArray(typeof(int)); }
  • 8. 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)); }
  • 9. int[] Filter(int[] ints) {     ArrayList results = new ArrayList();     foreach (int i in ints)     { if ( i > 3 ) { results.Add(i); }     }     return results.ToArray(typeof(int)); }
  • 10. interface IIntegerPredicate { bool Matches(int value); }
  • 11. class EvenPredicate : IIntegerPredicate { bool Matches(int value) { return value % 2 == 0; } }
  • 12. class GreaterThanThreePredicate : IIntegerPredicate { bool Matches(int value) { return value > 3; } }
  • 13. 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)); }
  • 14. int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints, new EvenPredicate()); int[] greaterThanThree = Filter(ints, new GreaterThanThreePredicate());
  • 15. interface IIntegerPredicate { bool Matches(int value); }
  • 17. bool Even(int value) { return value % 2 == 0; }
  • 18. bool GreaterThanThree(int value) { return value > 3; }
  • 19. 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)); }
  • 20. int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints,  new IntegerPredicate(Even)); Int[] greaterThanThree = Filter(ints,  new IntegerPredicate(GreaterThanThree));
  • 22. Inference int[] ints = new int[] {1, 2, 3, 4, 5 }; int[] even = Filter(ints,  new IntegerPredicate(Even)); Int[] greaterThanThree = Filter(ints,  new IntegerPredicate(GreaterThanThree));
  • 23. 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’t have to write it.
  • 24. Generics delegate bool IntegerPredicate(int value);
  • 25. Generics delegate bool Predicate<T> (T value);
  • 26. 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)); }
  • 27. 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(); }
  • 28. 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; }
  • 29. 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; }
  • 30. Iterators IEnumerable<T> Filter<T>(IEnumerable<T> values, Predicate<T> predicate) { foreach (T i in value) { if (predicate(i)) { yield return i; } } }
  • 31. Anonymous Methods IEnumerable<int> greaterThanThree = Filter(ints, GreaterThanThree);
  • 32. Anonymous Methods IEnumerable<int> greaterThanThree = Filter(ints, delegate(int value) { return value > 3; });
  • 33. 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.
  • 35. Lambdas int minimumValue = 3; IEnumerable<int> greaterThanThree = Filter(ints, delegate(int value) { return value > minimumValue; });
  • 36. Lambdas int minimumValue = 3; IEnumerable<int> greaterThanThree = Filter(ints, value => value > minimumValue);
  • 37. More Type Inference int minimumValue = 3; IEnumerable<int> greaterThanThree = Filter(ints, value => value > minimumValue);
  • 38. More Type Inference int minimumValue = 3; var greaterThanThree = Filter(ints, value => value > minimumValue);
  • 39. Extension Methods int minimumValue = 3; var greaterThanThree = Filter(ints, value => value > minimumValue);
  • 40. Extension Methods int minimumValue = 3; var greaterThanThree = ints.Filter(value => value > minimumValue);
  • 41. Anonymous Types var anonymous = new { Foo = 1, Bar = “Bar” }
  • 42. LINQ
  • 43. LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc.
  • 44. LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc. System.Linq Extension methods Where, Select, OrderBy etc.
  • 45. LINQ New delegates in System namespace Action<T>, Action<T1, T2>, Func<TResult>, Func<T1, TResult> etc. System.Linq Extension methods Where, Select, OrderBy etc.   Some compiler magic to translate sql style code to method calls
  • 46. 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                                       
  • 47. A (little) bit of theory
  • 53. Transformational Mindset We can just pass functions around instead in most cases - find an example where it still makes sense to use the GOF approach though.
  • 54. Input -> ??? -> ??? -> ??? ->  Output
  • 56. So why should you care?
  • 57. Functional can fill in the gaps in OO code  
  • 58. Abstractions over common operations means less code and less chances to make mistakes
  • 59. So what do we get out of the box?
  • 61.         people.Select(person => person.Name)    
  • 62.       people.SelectMany(person => person.Pets)
  • 64.              people.Where(person => person.HasPets)  
  • 66.       people.Take(5)
  • 67.       people.Skip(5)
  • 68.                    people.TakeWhile(person =>                               person.Name != &quot;David&quot;)
  • 69.                    people.SkipWhile(person =>                                   person.Name != &quot;David&quot;)
  • 71.   people.Select(person => person.Name) .Distinct()
  • 73.       people.Intersect(someOtherPeople)
  • 74.       people.Except(someOtherPeople)
  • 76.       people.OrderBy(person => person.Name)    
  • 77.       people.GroupBy(person => person.Name)
  • 79.       people.Count()
  • 80.       people.Select(person => person.Age) .Sum()
  • 81.       people.Select(person => person.Age) .Min()
  • 82.       people.Select(person => person.Age) .Max()
  • 83.       people.Select(person => person.Age) .Average()
  • 84.   Things can get more complex
  • 85.              people.Select(person => person.Age)              .Aggregate(0, (totalAge, nextAge) =>                       nextAge % 2 == 0                           ? nextAge + totalAge                           : totalAge)
  • 86.               people.Join(addresses,                           person => person.PersonId,                    address => address.PersonId,                      (person, address) => new {                                           person, address})
  • 87. We can just pass functions around instead in most cases - find an example where it still makes sense to use the GOF approach though.
  • 88.  
  • 89.     public class SomeObject { private readonly IStrategy strategy; public SomeObject(IStrategy strategy) { this.strategy = strategy; } public void DoSomething(string value) { strategy.DoSomething(value); } }
  • 90.     public class Strategy : IStrategy { public void DoSomething(string value) { // do something with string } }
  • 91.     public class SomeObject { private readonly Action<string> strategy; public SomeObject(Action<string> strategy) { this.strategy = strategy; } public void DoSomething(string value) { strategy(value); } }
  • 92.   Hole in the middle pattern
  • 93.     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; } }
  • 94.     public class CachedService : ServiceCache<IService> { public MyResult GetMyResult(MyRequest request) {           return FromCacheOrService(()                   => service.GetMyResult(request), request); } }
  • 96.   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));
  • 98.   static void Identity<T>(T value, Action<T> k) { k(value); }
  • 99.   Identity(&quot;foo&quot;, s => Console.WriteLine(s));
  • 100.   Identity(&quot;foo&quot;, s => Console.WriteLine(s)); as compared to var foo = Identity(“foo”); Console.WriteLine(foo);
  • 101.   public ActionResult Submit(string id, FormCollection form) {    var shoppingBasket = CreateShoppingBasketFrom(id, form);      return IsValid(shoppingBasket, ModelState,          () => RedirectToAction(&quot;index&quot;, &quot;ShoppingBasket&quot;, new { shoppingBasket.Id} ),   () => LoginUser(shoppingBasket,                  () =>                  {                    ModelState.AddModelError(&quot;Password&quot;, &quot;User name/email address was incorrect - please re-enter&quot;);                      return RedirectToAction(&quot;index&quot;, &quot;&quot;ShoppingBasket&quot;, new { Id = new Guid(id) });                 }, user =>                  {                    shoppingBasket.User = user;                      UpdateShoppingBasket(shoppingBasket);                      return RedirectToAction(&quot;index&quot;, &quot;Purchase&quot;,    new { Id = shoppingBasket.Id }); }         )); }
  • 102.   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); }
  • 104. So what could possibly go wrong?   http://icanhascheezburger.files.wordpress.com/2009/06/funny-pictures-cat-does-not-think-plan-will-fail.jpg
  • 105. Hard to diagnose errors  
  • 106. var people = new [] { new Person { Id=1, Address = new Address { Road = &quot;Ewloe Road&quot; }}, new Person { Id=2}, new Person { Id=3, Address = new Address { Road = &quot;London Road&quot;}} }; people.Select(p => p.Address.Road); 
  • 109. public T Tap(T t, Action action)  {     action();     return t; }
  • 110. people     .Select(p => Tap(p, logger.debug(p.Id))     .Select(p => p.Address.Road); 
  • 112. Lazy evaluation can have unexpected consequences  
  • 113.           IEnumerable<string> ReadNamesFromFile()         {             using(var fileStream = new FileStream(&quot;names.txt&quot;,                                                          FileMode.Open))             using(var reader = new StreamReader(fileStream))             {                 var nextLine = reader.ReadLine();                 while(nextLine != null)                 {                     yield return nextLine;                     nextLine = reader.ReadLine();                 }             }         }
  • 114.           IEnumerable<Person> GetPeople()         {             return ReadNamesFromFile()                      .Select(name => new Person(name));         }
  • 115.         IEnumerable<Person> people = GetPeople();           foreach (var person in people)      {          Console.WriteLine(person.Name);      }            Console.WriteLine(&quot;Total number of people: &quot; +                                      people.Count());
  • 116. Encapsulation is still important  
  • 117.   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()
  • 118. Linq isn't the problem here, it's where we have put it  
  • 119. Company naturally has the responsibility so encapsulate the logic here
  • 120. class Company {     public int TotalSalary     {         get          {             return employees.Select(e =>                                      e.Salary).Sum();         }     } }
  • 121. Sometimes we need to go further  
  • 122. If both Company and Division have employees do we duplicate the logic for total salary?
  • 123. IEnumerable<T> and List<T> make collections easy but sometimes it is still better to create a class to represent a collection
  • 124.   class EmployeeCollection {     private List<Employee> employees;        public int TotalSalary     {         get          {             return employees.Select(e => e.Salary).Sum();          }     } }
  • 126.  
  • 127. Mike Wagg mikewagg.blogspot.com [email_address] Mark Needham markhneedham.com [email_address]

Editor's Notes

  • #31: Lazy evaluation
  • #35: Go faster!!!
  • #48: origins of functional programming are found in lambda calculation/maths
  • #50: functions that take in a function or return a function. Need to have first class functions in the language to do that. We have that with all the LINQ methods - select, where, and so on.
  • #51: the whole premise of functional programming with side effect free functions assumes that we have immutable data. We can&apos;t achieve this idiomatically in C# because the language isn&apos;t really designed for it. I want to put an example of how immutability is easy in F#, can that go in this section?
  • #52: iterators in C# do this with yield keyword It&apos;s not necessary to have lazy evaluation to be functional but it&apos;s a characteristic of some functional languages.
  • #54: seems quite obvious but the most extreme guideline to follow is that we shouldn&apos;t need to store anything in variables. Look at the data as a whole if we don&apos;t store any intermediate values then we truly do have some data that we are passing through different filters and applying some transformation
  • #56: it&apos;s quite like the pipes and filters architectural pattern in fact. This is the way that we can combine functions on the unix command line.
  • #93: what is CPS?   is where we pass in a function that represents the rest of the program which will be called with the result of another function.
  • #96: what is CPS?   is where we pass in a function that represents the rest of the program which will be called with the result of another function.
  • #98: what is CPS?   is where we pass in a function that represents the rest of the program which will be called with the result of another function.
  • #99: the idea is that the rest of the program is contained in the continuation so we don&apos;t need to come back to the call site.
  • #100: the idea is that the rest of the program is contained in the continuation so we don&apos;t need to come back to the call site.
  • #101: the idea is that the rest of the program is contained in the continuation so we don&apos;t need to come back to the call site.
  • #103: Encapsulates the state but over complicates the program flow perhaps
  • #104: Encapsulates the state but over complicates the program flow perhaps