mixing functional and object oriented approaches to programming in c#

Post on 10-May-2015

2.316 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

A talk I did at DDD8, January 20th 2010.

TRANSCRIPT

Mixing functional and object oriented approaches to

programming in C#

Mark Needham

A bit of context

ThoughtWorks delivery projects

Web applications with somewhat complicated domains

5-15 developers on a team

Projects running for 6-12 months

All this means that we want to write code which is…

Easy to understand

Easy to change

Which leads us to what this talk is all about…

Organisation of code

‘The Lush Landscape of Languages’ - The ThoughtWorks Anthology

Rebecca Parsons

How might functional programming help us with that?

First class functions

“A programming language is said to support first class functions if functions

can be created during the execution of a program, stored in data structures,

passed as arguments to other functions, and returned as the values of other

functions”

Wikipedia

Immutability

Lazy evaluation

Recursion

Pattern matching

This is all very cool but…

Object Oriented design still has its place

Encapsulation

Abstraction

So how do these two paradigms work together?

Programming in the small/medium/large

http://weblogs.asp.net/podwysocki/archive/2009/12/14/going-hybrid-implementing-a-shopping-cart-in-f.aspx

In the large…

“a high level that affects as well as crosscuts multiple

classes and functions”

In the medium…

“a single API or group of related APIs in such things as classes, interfaces, modules”

In the small…

“individual function/method bodies”

Large

Medium

Small

Large

Medium

Small

LINQ

a.k.a.Functional collection parameters

http://billsix.blogspot.com/2008/03/functional-collection-patterns-in-ruby.html

“powerful abstractions over collections”

“the use of high-order functions is extremely useful for separating the collection's concerns from the user

of the collection”

for loop becomes less useful

Don’t just use ForEach!

Code becomes more declarative

Declarative?

“a statement specifies some aspect of the desired answer with no notion of how that answer is to

be determined”

‘The Lush Landscape of Languages’ - The ThoughtWorks Anthology

Rebecca Parsons

Requires a mental shift from imperative thinking

Transformational mindset

Patrick Logan in the comments sectionhttp://www.markhneedham.com/blog/2010/01/20/functional-collectional-parameters-some-thoughts/

Current Input

???

???

???

Desired Output

Going from one collection to another

var words = new List<string> { “hello”, “world” };

var upperCaseWords = new List<string>();foreach (var word in words){

upperCaseWords.Add(word.ToUpper());}

a.k.a. map

QuickTime™ and a decompressor

are needed to see this picture.

“hello”, “world”

????

“HELLO”, “WORLD”

“hello”, “world”

Select

“HELLO”, “WORLD”

var words = new List<string> { “hello”, “world” };

var upperCaseWords = words.Select(w => w.ToUpper());

Remove values we don’t want

var words = new List<string> {“hello”, “world”};

var wordsWithH = new List<string>();foreach (var word in words){

if(word.Contains(“h”)wordsWithH.Add(word);

}

a.k.a. filter

QuickTime™ and a decompressor

are needed to see this picture.

“hello”, “world”

????

“hello”

“hello”, “world”

Where

“hello”

var words = new List<string> {“hello”, “world”} ;

var wordsWithH = words.Where(w => w.Contains(“h”));

Summing some values

var values = new List<int> { 1,2,3 };

var total = 0;foreach (var value in values){

total += value;}

a.k.a. reduce

QuickTime™ and a decompressor

are needed to see this picture.

1, 2, 3

???

6

1, 2, 3

Sum

6

var values = new List<int> { 1,2,3 };

var total = values.Sum(v => v);

Some examples from projects

Getting the first value that matches a criteria

Foo(“mark”, true), Foo(“dave”, false), Foo(“mike”, true)

????

Foo(“mark”, true)

Foo(“mark”, true), Foo(“dave”, false), Foo(“mike”, true)

Where

Foo(“mark”, true), Foo(“mike”, true)

???

Foo(“mark”, true)

Foo(“mark”, true), Foo(“dave”, false), Foo(“mike”, true)

Where

Foo(“mark”, true), Foo(“mike”, true)

First

Foo(“mark”, true)

var foos = new List<Foo> { new Foo(“mark”, true), new Foo(“dave”, false), new Foo(“mike”, true) };

var firstSpecialFoo = foos. Where(f => f.HasSpecialFlag()). First());

var foos = new List<Foo> { new Foo(“mark”, true), new Foo(“dave”, false), new Foo(“mike”, true) };

var firstSpecialFoo = foos.First(f => f.HasSpecialFlag());

Removing some columns from a dataset

a,b,c,d,e,f

????

“a,d,e,f”

a,b,c,d,e,f

Where

IEnumerable of a, d, e, f

???

“a,d,e,f”

a,b,c,d,e,f

Where

IEnumerable of a, d, e, f

???

String.Join on [a, d, e, f]

“a,d,e,f”

a,b,c,d,e,f

Where

IEnumerable of a, d, e, f

ToArray()

String.Join on [a, d, e, f]

“a,d,e,f”

var aRow = “a, b, c, d, e, f”;

var newRow = String.Join(“,”, aRow

.Split(‘,’) .Where((_, idx) => !(idx == 1 || idx == 2)) .ToArray());

var aRow = “a, b, c, d, e, f”;

var rowWithColumnsRemoved = aRow

.Split(‘,’) .Where((_, idx) => !(idx == 1 || idx == 2)) .ToArray());

var newRow = String.Join(“,”, rowWithColumnsRemoved);

Checking if any parameters are null

public class SomeObject{ public SomeObject(string p1, string p2, string p3) { if (p1 == null) throw new Exception(...); if (p2 == null) throw new Exception(...); if (p3 == null) throw new Exception(...);

// rest of constructor logic }}

public class SomeObject{ public SomeObject(string p1, string p2, string p3) { var params = new List<string> {p1, p2, p3}; if (params.Any(p => p == null) throw new Exception(...);

// rest of constructor logic }}

Some lessons from the wild

Dealing with the null collection

public int SumNumbers(List<int> list){if(list == null)

return 0;return list.Sum(v => v);

}

public IEnumerable<T> EmptyIfNull(this IEnumerable<T> collection)

{if(collection == null)

return new List<T>();return collection;

}

Chris Ammerman in the comments sectionhttp://www.markhneedham.com/blog/2009/06/16/functional-collection-

parameters-handling-the-null-collection/

public int SumNumbers(List<int> list){

return list.EmptyIfNull().Sum(v => v);

}

LINQ and the forgotten abstraction

Avoid passing lists around

public class Quote{public List<Coverage> Coverages {

get; set; }

}

Later on in the view…

<% var coverages = Model.Quote.Coverages; %>

<% if(coverages.Where(c => c.Name == “coverageType1”) %>

...

<% if(coverages.Where(c => c.Name == “coverageType2”) %>

...

<% if(coverages.Where(c => c.Name == “coverageType3”) %>

Objects as the mechanism for encapsulation

public class Coverages{

private readonly List<Coverage> coverages;

public Coverages(List<Coverage> coverages){

this.coverages = new List<Coverage>(coverages);}

public Coverage CoverageType1{ get {

return coverages.Where(c => c.Name == “coverageType1”;

}}

}

LINQ is duplication too!

public class Coverages{

public Coverage CoverageType1{ get {

return coverages.Where(c => c.Name == “coverageType1”;

}}

public Coverage CoverageType2{ get {

return coverages.Where(c => c.Name == “coverageType2”;

}}

}

public class Coverages{

public Coverage CoverageType1{

get { return CoverageByName(“coverageType1”); }

}

public Coverage CoverageType2{

get { return CoverageByName(“coverageType2”); }

}

public Coverage CoverageBy(string name){

return coverages.Where(c => c.Name == name);}

}

Don’t forget “extract method”

var someFoos = new List<Foo>()// put some foos in the list

someFoos.Select(f => new NewFoo {

Property1 = f.Property1...

});

var someFoos = new List<Foo>()// put some foos in the list

someFoos.Select(f => CreateNewFooFrom(f));

Naming lambda variables

Putting functions in maps

public void SomeMethodParsing(string input){

if (input == “input1”) {

someMethod(input);}else if (input == “input2”){

someOtherMethod(input);}// and so on

}

Dictionary<string, Action<string>> inputs = new Dictionary<string, Action<string>> { { “input1” , someMethod }, { “input2” , someOtherMethod } };

public void SomeMethodParsing(string input){

var method = inputs[input];method(input);

}

Large

Medium

Small

Using functions to simplify some GOF patterns

Action

Func

public class SomeObject{private readonly IStrategy strategy;

public Foo(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 Foo(Action<string> strategy){

this. strategy = strategy;}

public void DoSomething(string value){

strategy(value);}

}

Need to ensure we don’t lose readability/understandability

The hole in the middle pattern

Brian Hurthttp://enfranchisedmind.com/blog/posts/the-hole-in-the-middle-pattern/

Common beginning and end.Only the middle differs

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);

}}

Other ideas I’m intrigued about

The option type

The nested closure

File.open(“someFile.txt”, ‘w’) do |out|out << “add something to file”

end

public class MyFile { public static void Open(string filePath,

Action<StreamWriter> block) { using(var writer = new StreamWriter()) { block(writer ); } }}

MyFile.Open(“c:\\mark.txt”, f => f.WriteLine(“some random text”));

Writing extension methods to create functional abstractions

Learning more

QuickTime™ and a decompressor

are needed to see this picture.

3 things to take away

The transformational mindset

Objects are still the mechanism for encapsulation

Simplify GOF design patterns by using functions

Thanks to…

• Dave Cameron (not that one!) who I worked with on the structure and general content of the talk.

• Mike Wagg, Brian Blignaut, Chris Owen for reviewing the talk and giving their input.

Questions?

Mark Needham

http://www.markhneedham.com/blog/mneedham@thoughtworks.comTwitter: markhneedham

top related