functional programming in c#

62
Functional Programming in C# NCrafts 21-22 May, Paris 2015 @tjaskula

Upload: thomas-jaskula

Post on 06-Aug-2015

140 views

Category:

Software


2 download

TRANSCRIPT

Functional Programming in C#NCrafts 21-22 May, Paris 2015

@tjaskula

What is functional programming ?

• Functions as building blocks• Function returns values only based on the passed input• Recursion• HOF (Higher Order Functions)• Creation of anonymous functions, in-line, lambda expressions• Closures• Immutability

What REALY is FP ?

“Natural way of telling the computer what it should do, by describing properties of a given problem in a concise language”

Programmers responsibility in FP ?

Specify functions to describe a given set of problems

Object Oriented Programming

• Encapsulation• Combining data and behavior into classes and objects• Modeling Real-World

Relationship FP to OOP

• No silver bullet• Programmer needs to understand different techniques

Why to talk about ?

• Concurrency programming models• Writing software it’s a complex process• Managing side effects

Part 1 – Make it simple

Composing dependencies

The OOP way

IoC container granted from the start.Developers argue about which framework to chose and not the problem to solve.

public class EnrollmentCommandHandler { private readonly StudentRepository _studentRepository; private readonly ClassRepository _classRepository; private readonly StudentArchiveRepository _studentArchiveRepository;

public EnrollmentCommandHandler(StudentRepository studentRepository, ClassRepository classRepository, StudentArchiveRepository studentArchiveRepository) { _studentRepository = studentRepository; _classRepository = classRepository; _studentArchiveRepository = studentArchiveRepository; }

public void Enroll(StudentEnrollCommand command) { var student = _studentRepository.GetById(command.StudentId); var @class = _classRepository.GetById(command.ClassId);

try { student.TryEnrollIn(@class); @class.TryEnroll(student);

student.Enroll(@class); @class.Enroll(student); } catch (Exception e) { // log }}

New requirements

Log, Security, Audit, Cache…Cross Cutting Concerns

public class EnrollmentCommandHandler { private readonly StudentRepository _studentRepository; private readonly ClassRepository _classRepository; private readonly StudentArchiveRepository _studentArchiveRepository; private readonly UnitOfWork _unitOfWork; private readonly EnrollementNotificationService _notificationService; private readonly ILogger _logger; private readonly AuthorizationService _authorizationService; private readonly CalendarService _calendarService; private readonly ServiceFoo _serviceFoo; private readonly ServiceBlah _serviceBlah; private readonly FactoryFoo _facoFactoryFoo; private readonly FactoryBlah _factoryBlah;

public EnrollmentCommandHandler(StudentRepository studentRepository, ClassRepository classRepository, StudentArchiveRepository studentArchiveRepository, UnitOfWork unitOfWork, EnrollementNotificationService notificationService, ILogger logger, AuthorizationService authorizationService, CalendarService calendarService, ServiceFoo serviceFoo, ServiceBlah serviceBlah, FactoryFoo facoFactoryFoo, FactoryBlah factoryBlah ) { _studentRepository = studentRepository; _classRepository = classRepository; _studentArchiveRepository = studentArchiveRepository; _unitOfWork = unitOfWork; _notificationService = notificationService; _logger = logger; _authorizationService = authorizationService; _calendarService = calendarService; _serviceFoo = serviceFoo; _serviceBlah = serviceBlah; _facoFactoryFoo = facoFactoryFoo; _factoryBlah = factoryBlah; } }

public void Handles(StudentEnrollCommand command) { var student = _studentRepository.GetById(command.StudentId); var @class = _classRepository.GetById(command.ClassId);

try { _unitOfWork.BeginTransaction(); student.TryEnrollIn(@class); @class.TryEnroll(student);

student.Enroll(@class); @class.Enroll(student); _notificationService.Notify(student); _unitOfWork.Commit(); } catch (Exception e) { _logger.LogError(e); } }

…or better… AOP to the rescue

[Logable] [Authorizable] [Cachable] [ExceptionPolicy] [Blablable] public class EnrollmentCommandHandler { private readonly StudentRepository _studentRepository; private readonly ClassRepository _classRepository; private readonly StudentArchiveRepository _studentArchiveRepository;

public EnrollmentCommandHandler(StudentRepository studentRepository, ClassRepository classRepository, StudentArchiveRepository studentArchiveRepository) { _studentRepository = studentRepository; _classRepository = classRepository; _studentArchiveRepository = studentArchiveRepository; }

public void Handles(StudentEnrollCommand command) { var student = _studentRepository.GetById(command.StudentId); var @class = _classRepository.GetById(command.ClassId);

try { student.TryEnrollIn(@class); @class.TryEnroll(student);

student.Enroll(@class); @class.Enroll(student); } catch (Exception e) { // log } }

Yes, but container can do more !

AOP with interceptionvar calculator = new Calculator();var calculatorProxy = Intercept.ThroughProxy<ICalculator>(calculator, new InterfaceInterceptor(), new[] { new LogBehavior() });

but one must :

• know Dynamic Proxy pattern• know the difference of Instance and Type Interceptors• know Interception behaviors• not forget VIRTUAL keyword on methods• wire up IoC container correctly

Really ?!!! Is this…

SIMPLE ?

DI flavors

IoC with conventions...

Scan(x =>{

x.TheCallingAssembly(); 

x.ExcludeNamespaceContainingType<IEvent>();x.ExcludeNamespaceContainingType<SearchModel>();x.ExcludeNamespaceContainingType<AuthenticationService>();x.ExcludeNamespaceContainingType<DovetailController>();

 x.AddAllTypesOf<IDomainMap>();

 x.WithDefaultConventions();x.With<DomainEntityAliaser>();

            });

DI flavors

IoC with conventions...

Scan(x =>{

x.TheCallingAssembly(); 

x.ExcludeNamespaceContainingType<IEvent>();x.ExcludeNamespaceContainingType<SearchModel>();x.ExcludeNamespaceContainingType<AuthenticationService>();x.ExcludeNamespaceContainingType<DovetailController>();

 x.AddAllTypesOf<IDomainMap>();

 x.WithDefaultConventions();x.With<DomainEntityAliaser>();

            });

DI flavors

IoC with manual configuration...

var container = new UnityContainer();container.RegisterType<IService, Service>(“Service”);container.RegisterType<IService,ServiceDecorator>( new InjectionConstructor(new ResolvedParameter(typeof(IService), “Service”)));

Func<IUnityContainer, object> factoryFunc = c => new ServiceDecorator(new Service( c.Resolve<ISubServiceProvider(); ));

container.AddNewExtension<DecoratorContainerExtension>(); container.RegisterType<IService, ServiceDecorator>(); container.RegisterType<IService, Service>();

DI flavors

IoC with manual configuration...

var container = new UnityContainer();container.RegisterType<IService, Service>(“Service”);container.RegisterType<IService,ServiceDecorator>( new InjectionConstructor(new ResolvedParameter(typeof(IService), “Service”)));

Func<IUnityContainer, object> factoryFunc = c => new ServiceDecorator(new Service( c.Resolve<ISubServiceProvider(); ));

container.AddNewExtension<DecoratorContainerExtension>(); container.RegisterType<IService, ServiceDecorator>(); container.RegisterType<IService, Service>();

DI flavors

IoC with XML configuration...

<unity> <typeAliases> <typeAlias alias="string" type="System.String, mscorlib" /> <typeAlias alias="ILogger" type="UnitySamples.ILogger, UnitySamples" /> <typeAlias alias="ConsoleLogger" type="UnitySamples.ConsoleLogger, UnitySamples" /> <typeAlias alias="DebugLogger" type="UnitySamples.DebugLogger, UnitySamples" /> <typeAlias alias="IContext" type="UnitySamples.IContext, UnitySamples" /> <typeAlias alias="UnityContext" type="UnitySamples.UnityContext, UnitySamples" /> <typeAlias alias="CustomerTasks" type="UnitySamples.CustomerTasks, UnitySamples" /> </typeAliases> <containers> <container> <types> <type type="ILogger" mapTo="ConsoleLogger" name="defaultLogger"/> <type type="ILogger" mapTo="DebugLogger" name="debugLogger"/> <type type="IContext" mapTo="UnityContext"> <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration"> <constructor> <param name="logger" parameterType="ILogger"> <dependency name="debugLogger"/> </param> </constructor> </typeConfig> </type> <type type="CustomerTasks"> <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration"> <constructor> <param name="context" parameterType="IContext"> <dependency/> </param> </constructor> </typeConfig> </type> </types> </container> </containers> </unity>

DI flavors

IoC with XML configuration...

<unity> <typeAliases> <typeAlias alias="string" type="System.String, mscorlib" /> <typeAlias alias="ILogger" type="UnitySamples.ILogger, UnitySamples" /> <typeAlias alias="ConsoleLogger" type="UnitySamples.ConsoleLogger, UnitySamples" /> <typeAlias alias="DebugLogger" type="UnitySamples.DebugLogger, UnitySamples" /> <typeAlias alias="IContext" type="UnitySamples.IContext, UnitySamples" /> <typeAlias alias="UnityContext" type="UnitySamples.UnityContext, UnitySamples" /> <typeAlias alias="CustomerTasks" type="UnitySamples.CustomerTasks, UnitySamples" /> </typeAliases> <containers> <container> <types> <type type="ILogger" mapTo="ConsoleLogger" name="defaultLogger"/> <type type="ILogger" mapTo="DebugLogger" name="debugLogger"/> <type type="IContext" mapTo="UnityContext"> <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration"> <constructor> <param name="logger" parameterType="ILogger"> <dependency name="debugLogger"/> </param> </constructor> </typeConfig> </type> <type type="CustomerTasks"> <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration"> <constructor> <param name="context" parameterType="IContext"> <dependency/> </param> </constructor> </typeConfig> </type> </types> </container> </containers> </unity>

DI flavors

IoC with XML configuration...

<unity> <typeAliases> <typeAlias alias="string" type="System.String, mscorlib" /> <typeAlias alias="ILogger" type="UnitySamples.ILogger, UnitySamples" /> <typeAlias alias="ConsoleLogger" type="UnitySamples.ConsoleLogger, UnitySamples" /> <typeAlias alias="DebugLogger" type="UnitySamples.DebugLogger, UnitySamples" /> <typeAlias alias="IContext" type="UnitySamples.IContext, UnitySamples" /> <typeAlias alias="UnityContext" type="UnitySamples.UnityContext, UnitySamples" /> <typeAlias alias="CustomerTasks" type="UnitySamples.CustomerTasks, UnitySamples" /> </typeAliases> <containers> <container> <types> <type type="ILogger" mapTo="ConsoleLogger" name="defaultLogger"/> <type type="ILogger" mapTo="DebugLogger" name="debugLogger"/> <type type="IContext" mapTo="UnityContext"> <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration"> <constructor> <param name="logger" parameterType="ILogger"> <dependency name="debugLogger"/> </param> </constructor> </typeConfig> </type> <type type="CustomerTasks"> <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration"> <constructor> <param name="context" parameterType="IContext"> <dependency/> </param> </constructor> </typeConfig> </type> </types> </container> </containers> </unity>

APPROVEDby

System Administr

ator

What problem do we try to solve ?

• Decoupling ?• Testing ?• Modular Design ?• Dependencies management ?

Dependencies are bad....

public class EnrollmentCommandHandler { public EnrollmentCommandHandler(StudentRepository studentRepository, ClassRepository classRepository, StudentArchiveRepository studentArchiveRepository, UnitOfWork unitOfWork, EnrollementNotificationService notificationService, ILogger logger, AuthorizationService authorizationService, CalendarService calendarService, ServiceFoo serviceFoo, ServiceBlah serviceBlah, FactoryFoo facoFactoryFoo, FactoryBlah factoryBlah ) { _studentRepository = studentRepository; _classRepository = classRepository; _studentArchiveRepository = studentArchiveRepository;

Cyclic dependencies are evil....

public class EnrollmentCommandHandler { public EnrollmentCommandHandler(StudentRepository studentRepository, ClassRepository classRepository, StudentArchiveRepository studentArchiveRepository, UnitOfWork unitOfWork, EnrollementNotificationService notificationService, ILogger logger, AuthorizationService authorizationService, CalendarService calendarService, ServiceFoo serviceFoo, ServiceBlah serviceBlah, FactoryFoo facoFactoryFoo, FactoryBlah factoryBlah ) { _studentRepository = studentRepository; _classRepository = classRepository; _studentArchiveRepository = studentArchiveRepository;

public class EnrollmentCommandHandler { private readonly StudentRepository _studentRepository; private readonly ClassRepository _classRepository; private readonly StudentArchiveRepository _studentArchiveRepository;

public EnrollmentCommandHandler(StudentRepository studentRepository, ClassRepository classRepository, StudentArchiveRepository studentArchiveRepository) { _studentRepository = studentRepository; _classRepository = classRepository; _studentArchiveRepository = studentArchiveRepository; }

public void Enroll(StudentEnrollCommand command) { var student = _studentRepository.GetById(command.StudentId); var @class = _classRepository.GetById(command.ClassId);

student.TryEnrollIn(@class); @class.TryEnroll(student);

student.Enroll(@class); @class.Enroll(student); }}

Code we have....

public class EnrollmentCommandHandler { private readonly StudentRepository _studentRepository; private readonly ClassRepository _classRepository; private readonly StudentArchiveRepository _studentArchiveRepository;

public EnrollmentCommandHandler(StudentRepository studentRepository, ClassRepository classRepository, StudentArchiveRepository studentArchiveRepository) { _studentRepository = studentRepository; _classRepository = classRepository; _studentArchiveRepository = studentArchiveRepository; }

public void Enroll(StudentEnrollCommand command) { var student = _studentRepository.GetById(command.StudentId); var @class = _classRepository.GetById(command.ClassId);

student.TryEnrollIn(@class); @class.TryEnroll(student);

student.Enroll(@class); @class.Enroll(student); }}

Code that matters…

The rest is Plumbing code

Functional way

int Multiply(int a, int b){

return a * b;}

Partial application

Func<int, int> multiplyBy10 = b => Multiply(10, b);

Functional wayint a = 3;int b = a + 7;int c = b * 10;

Composition

Func<int, int> calcCFromA = a => CalcC(CalcB(a));

int CalcCFromA(int a){

return (a + 7) * 10;}

int CalcCFromA(int a){

return CalcC(CalcB(a));}

let calcCFromA = calcB >> calcC

public class EnrollmentCommandHandler { private readonly StudentRepository _studentRepository; private readonly ClassRepository _classRepository; private readonly StudentArchiveRepository _studentArchiveRepository;

public EnrollmentCommandHandler(StudentRepository studentRepository, ClassRepository classRepository, StudentArchiveRepository studentArchiveRepository) { _studentRepository = studentRepository; _classRepository = classRepository; _studentArchiveRepository = studentArchiveRepository; }

public void Enroll(StudentEnrollCommand command) { var student = _studentRepository.GetById(command.StudentId); var @class = _classRepository.GetById(command.ClassId);

student.TryEnrollIn(@class); @class.TryEnroll(student);

student.Enroll(@class); @class.Enroll(student); }}

Refactor this…

public void Enroll(StudentRepository studentRepository, ClassRepository classRepository,

StudentEnrollCommand command){

var student = studentRepository.GetById(command.StudentId); var @class = classRepository.GetById(command.ClassId);

student.TryEnrollIn(@class); @class.TryEnroll(student);

student.Enroll(@class); @class.Enroll(student);}

to this…

var studentRepository = new StudentRepository();var classRepository = new ClassRepository();

var pipeline = c => handlers.Enroll(studentRepository, classRepository, c);

Bootstrap in one place…

_handlers.Dispatch(new StudentEnrollCommand(1));

=

pipeline(new StudentEnrollCommand(1));

Handle in another…

var studentRepository = new StudentRepository();var classRepository = new ClassRepository();

var pipeline= c =>handlers.Log(c, c1 =>

handlers.Enroll(studentRepository, classRepository, c1));

Want to log ?

var studentRepository = new StudentRepository();var classRepository = new ClassRepository();

var studentEnrollPipeline = c =>handlers.Audit(c, c1 => handlers.Log(c1, c2 =>

handlers.Enroll(studentRepository,classRepository, c2));

Audit ?

var lifeTime = new LifeTime();

var studentRepositoryFactory = () => new StudentRepository();var classRepositoryFactory = () => new ClassRepository();

var studentEnrollPipeline = c => handlers.Audit(c, c1 => handlers.Log(c1, c2 =>

handlers.Enroll(lifeTime.PerThread(studentRepositoryFactory), lifeTime.PerThread(classRepositoryFactory), c2));

Lifetime management ?

Why to do it ?

• 99% of time you don’t have a problem for IoC container• IoC makes easier things you shouldn’t be doing anyway• Feel the pain and think

To many dependencies ?

• You have another serious problem

Is this…

SIMPLE ?Is is just pure C# code

Part 2 – Control complexity

Composition strikes again…but in different shape

Writing a program using functions

How to order sequence of functions ?

ParseCopyright(string text) { return text + “©” }ParseAppendix(string text) { return text.Remove(“APPENDIX”); }

Solution

By composing them

ParseCopyright(ParseAppendix(text));

But this can fail

ParseAppendix function may throw an exception

Solution

Return two kind of things from ParseAppendix function

string string

Instead of Let’s allow

string

string

Error

or

But function can return one thing

And only one thing

Solution

Put in in the box

string ParseResult

What happens to this now ?

ParseCopyright(ParseAppendix(text));

string ParseResultParseAppendix ParseCopyright

is success

Solution

Let’s have a special “link”/”connect”/”compose” function

string ParseResultParseAppendix ParseCopyrightstring

Error

Connect

What we have seen is a…

M***D

Demo

Composition

Composition is the key to controlling complexity in software.

“In our study of program design, we have seen that expert programmers control the complexity of their designs with the same general techniques used by designers of all complex systems. They combine primitive elements to form compound objects, they abstract compound objects to form higher-level building blocks, and they preserve modularity by adopting appropriate large-scale views of system structure.”

Should I do functional programming in C# ?

To take away

• Be stupid! Don’t waste your brain on complicated code• Don’t waste your time to understand Rube Goldberg machines• Simplicity. Write only the code that matters.• Readability• Less bugs

Questions ?

Thanks