3. No Functional Programming experience is
required
Experience in C# or other Object Oriented
languages helps
Basic understanding of Trains
Prerequisites
4. Set the scene
Working on a team at AGL
Discussions about code cleanliness comes up
regularly
Wanted to help out
This is the story
8. Sad Path
Increases the size of
the code
Makes it less
readable
Losing the domain of
the code
9. ModelView Controller
Model View Controller (MVC)
first used at Xerox in the 1970s
Popularised on the web thanks
to Ruby on Rails.
Fat Controller and Fat Model
problem
10. The Service Layer
Enterprise Application
Architectures by Martin
Fowlers
Manipulate the model
Can the controller show us
the flow
12. Goals
Happy Path code without sacrificing error
checking
Remove the God Object and compose smaller
functions
Make the controller show the data flow
Introduce some Functional Programming
concepts
Learn something new and have fun!
14. C# and Functional
Programming
Lambda expressions
LINQ
Better tuple support,
Pattern matching
Read only collections
Local functions
etc
15. Functional ApproachTo Error
Handling Scott Wlashin
https://fsharpforfunandprofit.com/rop
Marcus Denny from ALT.NET
Vladimir Khorikov – Microsoft MVP from
Washington DC
https://enterprisecraftsmanship.com
The railway is the analogy
Happy path programming
18. Result
Holds either success or
failure
Can hold errors on failure
Holds a value on success
Is immutable
Recreated on each call
Can transform the type
Success Failure
Input
19. F# Results
Native support F# 4.1 (early 2017)
https://docs.microsoft.com/en-
us/dotnet/fsharp/language-
reference/results
Has Error and Ok as keywords
Can we do this in C# ?
22. ErrorType
Store all the business
errors in an enumeration
Forces you to handle all
errors
Keeps all the errors
together
Could be a big ugly in
size
23. Exceptions
Exceptions are for
exceptional
circumstances
Don’t use for control
flow
Good use for I/O errors
50. Developer Feedback
Liked the reduced nesting
Was more readable
Service layer broken down into reusable parts
Is showing dataflow better
Bit of a learning curve
Bit difficult to debug
Preferred the OnSuccess/OnFailure over Map,
Bind
51. State machines
Validation
Business logic
Lambdas/ Azure Functions
Good Use Cases
52. Code shows the Happy Path without sacrificing
error checking
Compose smaller reusable functions
The controller shows the data flow
Learned some minor Functional Programming
concepts
And have fun!
Recap Goals
Take us back a year from now. We we're happy developers coding away.
However, as usual, we kept having difficulties deciding what we considered to be "clean" code.
Difference between code cleanliness and code quality
After many meetings, and broken design patterns we opted to give a functional approach a go.
Raise your hand if you’ve ever had any discussions about code cleanliness
This was originally proposed as a solution to some of the issues we had discussing code cleanliness
Let’s see how it works and how it worked out
3 Mins
We’re going to implement a simple API
You could image it being what’s behind this technology you may have used
We’re not going to show some real world code.
None of this code is legit code, or for that matter; is intended to make any sense
It’s purpose is to convey a problem and show the solution
It’s something we could all be somewhat familiar with
1. Validate request
2. Charge the credit card
3. Update balance
4 Return receipt
Looks pretty simple
So, this is generally how your code starts off
Explain code
In reality we have to deal with validation, logging, errors, exceptions etc
Throw some null checks in
Test return conditions
Add some Try catches
Lots of if statements for error handling
Plus some logging, if you’re lucky
Can turn 6 lines of code in 19
How many people have this problem?
How can we make the code more readable without sacrificing our standard checks?
We’ve also have another problem
Were does the business logic go?
In the MVC design pattern, the controller manipulates the model and the view generates the content based off the model.
Create reusable business logic
Who remembers the fat model / fat controller arguments?
Do we put it all in the controller or the model?
This is something I’ve seen in projects for years.
“Where does my business logic lie”
After following Martins Fowlers advice or reading Enterprise Application Architectures you’ll probably be in favour of the service layer
But we can keep coming back to the same issue of the God Object
We’ve had solutions in the past but it keeps reoccurring
It’s coined the God Object, where all the important code lies where it’s just more connected to everything than it should be
Other patterns like MVVM explicitly separate the business logic a bit better
CQS seems with frameworks like the mediator patterns gets you a bit out of this problem
But we have we solves the problem
We want:
No separation of concern
Ease of reusability
By making and composing smaller functions
7 mins
F# code is pretty neat
We've got lots of different styles to programming such as Object Oriented (C#, Java), Event driven, Procedural, declarative etc
Railway oriented programming is more of a design pattern than a rule to the above
Functional programming is another which is slowly entering the mainstream more each year
This has affected our programming in some ways
For example, C# 3 brought us lambda expressions, LINQ and now we have better tuple support, pattern matching, expression bodied members, read only collections, local functions
Other libraries bought us ReactiveX with rx.net, language-ext
So why can’t we have a functional approach to error handlng
Can we have a functional approach to error handling?
Will it help show us the flow of the code to neaten it up?
It was originally popularised by Scott Wlaschin in his talk at NDC Oslo which is entirely in F#
The name railway is there to help explain the concept as a metaphor for how the errors are handled
Scott Wlashin also has blogged about ROP which is one of his top hits which you can check out further if you're
Other developers have also attempted to convert this into c#
My main inspiration was Marcus Denny from alt.net!
Vladimir Khorikov MVP from Washinton DC
Functional programming in C# course on pluralsight
It’s rather simple but takes a little practice
We want to code for the happy path
As the input comes in here, it enters the function
Which returns whether the result of that function was a success or failure
The tracks are you input and output into your function
Use switches to join the tracks together
We can have many different types of tracks and switches which compose all of the functions together
On the success line we can carry different types to be the next input
10 Mins
This is easiest to start with in my opinion
Either monad is either right or left
Holds either success or failure
When successful holds the returned data
Can hold errors on failure
Chain these two tracks together
Native support in F# 4.1 (2017)
Thanks to discriminated unions!
Mentioned by Microsoft in their documentation
Wont be covering F# only in C#
Alternative - Can store Tsuccess Tfailure to return a data in the failed component
15 mins
This way you can process it with more information
Store all the different types of errors in an enumeration
Keeps all the errors together
Serves as documentation
Forces you to handle all errors
Type safety for testing
Could be a big ugly in size
Who would prefer to return false, raise your hand?
Exceptions follow like a GOTO statement – hard to read
Don’t use it for control flow
Can be slower – compilers generally don’t optimise for exceptions as control flow
Generally used for I/O problems
Global Exception handlers
17 Mins
The railway analogy
Can we get it really the same a F# -> no
Two track function is a function which returns a result type
Useful for most cases
Single track function is just a function which returns a value
The easiest one, but can be a bit tricky to join
No failure
You can lift these with the switches
Single track function is just a function which returns a value
The easiest one, but can be a bit tricky to join
No failure
You can lift these with the switches
20 Mins
We’re connecting them up through extension methods based off of the result type
There are a few different types of switches
Explain Extension methods,
Explain func
Combiners aren’t explained in the next slides but the idea is you can connect say a Map and a
It’s not too hard
Explain extension method
Explain Func
If just an if statement on a result type to execute another function
Notice: The func return type
Notice: The switchfunction doesn’t use result as it retuns a result
The switch function can return a result which switches the tracks
An adapter that takes a switch function and creates a new function that accepts two-track values as input.
Can still transform types!
An adapter that takes a normal one-track function and turns it into a two-track function.
Switches are these maps but can change the success or fail based on the return type
Map is good for getting functions that done return a Result object to continue to follow the same path
Remember: The highlighted part is what is represented in this code
Notice: they both create a new result type on each switch. They cannot change tracks.
An adapter that takes two one-track functions and turns them into a single two-track function.
Same as map but executes on both branches
An adapter that takes a dead-end function and turns it into a one-track function that can be used in a data flow.
A constructor that takes a one-track value and creates a two-track value on the success branch.
Less used
A constructor that takes a one-track value and creates a two-track value on the success branch.
Less used
Coming back to the exception handling
These are a bit more custom, shown for an example
Exceptions one isn’t the best example, can I say pokemon exception
We can switch the result type here to the error path based on if an exception was throw or not
It’s a single track function because it has no result return type
This switch returns a success or fail
On success only
Can change tracks based on the single track return type
No result return type
Finally, you’ll need a adapter block to transfer the two tracks into some output
Not the best of examples
Tdata from result type can then become the returned data
This has to be kind of short and sweet to get the point across, but it’s not entirety accurate.
You can see how we’re returning a response based off of whether or not this was successful or not
Brings this together for output,
Lots of options for this depending on your desired outcome or where you intend to use this handle
30 Mins
Apply it to our problem
Chain together the functions with switches
Which will decrease the amount of error handling code
Making our controllers look pretty and understandable
Service layer will have reusable code
Lets see if we can make it happen
Easiest one to grasp first
There’s a few different ways we can approach this
Lets start with a bind, the most common and easiest.
We’ll create a two-track function for each
Could use the bool lift function shown earlier
Notice even though we’re returning a Two-Track, we excuse using the result with the _ score lambda
That underscore actually represents the accountNumber as in the previous example
We transform the type in the result with each call
We still have access to the scope of whatever has been passed in the parameters
No result prior to the first validate account call
I hope this is making a bit more sense
Again this exception code isn’t how you would implement it
Here we’ve actually used an expression bodied member which says our whole method is an expression!
You’re better off moving it into another method and have a result type returned but for the sake of a call you couldn’t change
On a successful result, update the card
Success or fail
Executes on the success branch
Return the same result with a different type
Remember: difference between the Map and the DoubleMap is that the double map will execute on both tracks
I know what you’re all thinking, where’s the handle
Before we get to that and wrap it all up, we have a bonus!
C# has operator overloading! We can give two methods the same name
All you legitimately do is name the methods OnSuccess, OnFailure, OnBoth, FailWith
Reduce the functional programming knowledge
Make it easier for developers to just type OnSuccess and provide what they want, it will find the method for you!
Reduces the thought process with no extra effort or errors
The money shot
It’s all the same Bind Map etc underneath
Reusable on other similar Actions
I could make another action that does something similar and join compose them together differently
It doesn’t look as cool as F# though
35 Mins
Come back to our story
We revaluated our ROP trial on the AGL team
Feedback was mostly positive
Siva: Liked how we no longer needed if statements. May want to use it again. Found it a bit tricky at first but felt others could understand it easily. Noticed it did break down the services into reusable functions and the action was showing dataflow better.
Anup: Thought the code was much more readable. Did notice that it broke down the service layer. Thought it was understandable at first but as the extension methods grew found it hard to work with. Thinks he needs more time to learn it and show a better understanding
Sudheerah: Found it a bit difficult to debug. Overall did find it more expressive.
Dylan: Once the extension methods started building up, he found it difficult to know what to use. There was a bit of a learning curve at the beginning to understand the flow of data. Felt that the flow of data was easy to read after it had been written.
Varan: Found the map bind confusing. Wasn’t a big fan of the functional wording. Preferred onsuccess on failure etc. Did like using it over if statements to check if something was successful or not.
Raghav: Thought the code turned out rather readable. Wanted to polish it up a bit more for future use.
Does it fit everywhere?
Validation could include like some sort of file parsing
40 mins
So if you’re not a fan of MVC, maybe you just want routes and commands to be processed instead
Becoming a bit more of a popular pattern
Single file please
If you’re interested further, learn functional programming