
Course Overview
Hi, Welcome to Functional Programming Deep Dive in C#. My name is Cory. I built this course for two reasons: There are many fewer well-built and curated resources online for learning to improve your craft, compared to resources for beginners or those just starting out. I feel that this course helps plug that gap.
Also because when I was trying to learn functional programming from a practical perspective, I found few resources that described functional programming in an approachable way with real applications to the code I was working on. I aim to do exactly that with this course.
Who is this course built for?
This course is built with the experienced developer in mind. I won’t be going over any functions of the C# language specification in detail, with the exception of C# 9. I will instead focus on the “why you should do this” and practical examples and ideas for using the knowledge contained herein in your current or future projects.
Some of the lessons will use concepts that a beginner might find advanced or overwhelming. I don’t want to discourage beginners from taking this course, but you should know what you are getting into.
To effectively understand the concepts and code examples in this course, you need to have knowledge of: delegates, lambda functions, the func- and action classes, the SOLID principles, and declarative vs. imperative programming.
Throughout this course, we will be building an example order processing system for an eCommerce site using our new functional knowledge. The screenshots you see on screen in the lessons are snippets from this project.
Please click “Ask me at the end of the course”
If you are watching this course on Udemy, they will soon ask you to rate this course. Probably in the next video or two. Please consider clicking “Ask me later” or “Ask me at the end” so you can form a better opinion about how valuable this course is to you. Courses live and die on Udemy by their ratings. I would really appreciate it if you did actually rate it. Thank you.
What is Functional Programming?
Functional programming is a programming paradigm. It is a different way of thinking about how to build programs. The features of the language that we use are just a means to an end.
In functional programming functions are first-class values. All that means is you can use functions just like variables. You can pass functions to and from other functions, and you can store them in variables or collections. We will discuss the true definition of a function in Functional programming later. It’s not what you think it is.
Functions in the application do not mutate their parameters or inputs. This means that values never change in-place. Functions in functional programming strictly map an input to output with no side effects.
Code written in functional programming style focuses on computing results instead of performing actions.
There is an example of what this means in the first lesson on pure functions.
So, why not use F#? If you can use F# in a project or you already know F#, then absolutely use it. It is a true functional programming language. But the world isn’t perfect and there will be situations in your career where you will not be able to use F# or another functional language, but you can still reap the benefits from functional programming.
Not everyone has the time or motivation to learn a new language. Not everyone has a say in what tech they get use for a project, or they are working on an existing system written in C# and want to improve the codebase using functional programming anyway.
Lastly, at least for me, there is a huge bank of knowledge that one acquires while working inside an ecosystem as the one Microsoft has built around C#. Using that existing knowledge, intuition, tooling, and ecosystem to take advantage of the benefits of functional programming is a more efficient use of time and resources.
Core Concepts
Functional programming has a couple of core concepts that define what it is. Those are:
Functional Purity.
First Class Functions.
Immutability.
Concurrency.
Referential Transparency.
Composability.
Why Use It?
Object oriented programming and functional programming can be used together. They are not mutually exclusive. The generally accepted rules for structuring complex applications AKA, design patterns transcend object oriented programming and functional programming. The generally accepted rules for structuring complex applications well, transcend object oriented programming and functional programming. The SOLID principles for example can be applied using both.
Using the concepts of functional programming results in code that is more modular. By definition, all pure functions are tightly encapsulated. They operate only on their inputs and produce no side effects. This allows loose coupling between components by design and requires little extra thought.
Separation of concerns is a little easier to achieve because writing pure functions guides you toward writing functions that do one thing.
Referential Transparency makes it easier to understand what a function is supposed to do just by the signature, and so makes it easier for you to separate your concerns without thinking too hard about the implementations of your functions.
Functional code is easier to understand and predict. It forces a declarative approach instead of an imperative approach. This means that we write functions that declare what is needed and what they will do instead of how they will do it. In imperative style, you declare computations that change the program’s state or commands that the computer must perform.
A good metaphor is like telling your contractor to build you a shed and letting him fill in the details vs telling your contractor where to put the footings, what studs to use and where they will go, and how to install the roof shingles.
Predictability also greatly reduces the mental burden of understanding code. If you have a test that confirms a function’s output for certain inputs, then you know you will always get the result you are testing. There will be code examples that describe this in later lessons.
Functional programming also simplifies the testing and debugging of your code. It is a natural benefit of keeping functions pure and declarative. If the function only maps inputs to outputs and has no side effects, and you control all the inputs as the caller; that makes testing as simple as passing in your test values. There is no need to make stubs or mocks, meaning we can ignore the internals of the function under test.
Pure functions are by definition thread-safe so concurrency is built in. You never have to worry about deadlocks or race conditions. That said, your entire application is unlikely to be composed of pure functions. There needs to be a state stored somewhere and when you are dealing with any type of I/O there will be impure functions at some point. The goal here is to minimize the surface area that these issues infect. By keeping as much of your program as pure as possible you reduce the surface area that you have to worry about.
Disadvantages
Now for some disadvantages. There is a higher learning curve for truly understanding and internalizing the use of functional programming. There aren’t as many learning resources so you have to extrapolate a lot more yourself - one of the reasons I made this course.
While writing pure functions is easy, composing them together into a complete program is much harder, especially at the beginning of your learning curve.
Writing functional code in C# will require you to think about programs differently. It is a challenge to change your thinking from imperative, or object oriented programming, to declarative and functional. You need to organize your code differently, process data differently, and write classes differently. It will take some getting used to, but the benefits far outweigh the costs.
Because we cannot mutate state, variables need to be copied more frequently to achieve immutability. This results in not only extra code, but also extra memory consumption. Any I/O operation will be impure by its nature which means that any pure function that MUST use I/O can no longer be a pure function. The concept of functional code/ imperative shell helps here, which we will discuss later.
Our Journey
This is a layout of the lessons in this course. The later lessons build significantly on the core concepts here. It would be beneficial for you to follow the order, but you can still get a lot out of this course if you don’t.
If you feel you have a very firm grasp on functional purity, immutability, using and building Lambda functions, and recursion then you may be able to skip ahead to the later lessons.
Each lesson will start with an explanation of the concept, why it is important, and where you can use it.
Then there will be a code example that we will walk through together that will help solidify the concept in your head. Then I will give you practical advice on where to use your new knowledge.
Each video will end with a practical refactor challenge for you to complete should you choose to do so. The refactor challenge will use a demo application to approximate a real-life situation.
The goal of each lesson is to introduce the concepts of functional programming using built-in C# language features.
Once you learn implementation in vanilla C#, I will introduce libraries and frameworks that make implementing the concept easier, if there are any.
This approach will allow you to get the maximum utility from this course whether you control your codebase or not - and can introduce new dependencies or not.
These lessons build on the core concepts and are not strictly functional programming concepts. They are best practices, patterns, shortcuts, or ideas about how to get the most out of functional programming with C#.
As a bonus, I have included lessons on design patterns and libraries that I found helpful when programming in C# with functional programming concepts.
If you don’t care who I am or what my background is you can skip the rest of this video.
Who am I?
I’ve been building software for 10 years in most levels of the industry. From small custom applications for mom and pop business, to industry-leading enterprise applications used by millions of people.
I have worked in many industries such as Healthcare, Logistics, e-Commerce, Meal delivery, Tax software, Wireless Carrier services, and Cybersecurity.
I’m an autodidact, which means I’m self-taught, and I have a compulsion to always be learning. I read constantly and have broad interests across the industry.
I strive to be a no-nonsense teacher so you will find no fluff in these lessons. Straight to the point so you can get on with your day.
I hope to pass on my knowledge and love for learning on to my peers and improve their code and expertise.
That’s all for this lesson, let’s jump to the next one.
What are Pure, Higher Order, and First Class Functions?
In this lesson we will go in depth into pure functions, higher order functions, and first class functions with a follow along coding exercise to help solidify the concept in your mind.
What are Functions
First let’s talk about functions. You may have a definition in your head of what a function in C# is. That definition is likely correct, but too broad when referring to a function in functional programming. A function in C# may go by a method, a procedure, or a subroutine. In functional programming a function has a strict definition.
Since functional programming came from Lambda Calculus, the definition of a function in functional programming comes from mathematics.
A function in functional programming is an instruction that has no scope and no global state. That last one can be tricky, because DateTime.Now is technically global state - Something you may not have considered.
What are Pure Functions
Functional Purity is one of the core concepts of functional programming. It means that a given function does not produce side effects, or have gotchas. A side effect is a state change in something outside the context of the currently executing function.
This means that the function should produce the same output for the same input, always.
A pure function computes a value, the same value every time for a given input.
Another interesting note is that pure functions can only call other pure functions. Any function that calls an impure function is itself impure.
So impurity is infectious.
If you have ever worked in a legacy codebase you’ve probably experienced a side effect.
Something that you didn’t expect a function to do based on its signature.
Any function that “Handles” or “Processes” probably produces side effects. These are usually accompanied with the thought “what the hell happened there?” or “why did THAT happen?”.
This also means that any function that does I/O operations, uses global variables, makes HTTP requests, or throws an exception is not pure.
You may be asking yourself “How do you write anything of consequence without doing any of that?”, the answer is wrapping your functional core in an imperative shell, which is a design pattern we will discuss in a later lesson.
So, why is throwing an exception considered a side effect? Throwing an exception is technically modifying global state. Understanding that exceptions are side effects forces you to handle adverse conditions in your function in a way that maintains purity.
This in turn contributes to easy of understanding in the code and easier testing.
On screen is an example of a pure function. It only operates on its inputs and the function signature makes it very clear what this function will do. It also does not mutate its own inputs. A nice trick is to make your class and all of its methods static, which helps prevent you from using instance state.
One benefit of pure functions is that you enforce referential transparency. Which means that you can determine what a function will do, and what result it will produce just by looking at its signature.
It will only operate on the parameters we give it and will not touch global or instance state. This makes it easier to reason about what a function will do without having to look at the function body.
Making understanding complex code a lot faster and easier.
What are first class and higher order functions
First class functions are functions that can be passed around your program as if they were variables. If you have ever used a lambda or the func or action classes, you have used a first class function.
Higher order functions build off the concept of first class functions and take it one step further. Higher order functions either are functions that either accept a function as an argument or return a function themselves. A common example is the map or filter methods from ES6 Javascript .
This is an example of a first class function on screen.
The taxRater func is the first class function.
The taxRater is responsible for determining the tax rate for the order.
Crucially, the OrderCalculator does not know anything about that process, the func handles all of it.
This allows flexibility in how to calculate tax and allows the caller to decide that functionality.
This taxRater function can be defined anywhere in the application and stored in a variable before this call happens and it will only be executed when this function needs it.
An example usage of such a class might look like this;
You can see the GetTotal method is passing in a function, the TaxRater, as if it was a variable. The TaxRater is defining the behavior of figuring out the tax. The GetTotal method knows nothing about it and doesn’t need to. While this is a trivial example, the power of this approach will become clear later.
Don’t worry - we will go in depth into all of this in the code example.
The example on top, while technically serviceable, is not truly a first class function since you cannot pass the TaxRater around even though you can use it as a variable.
One small refactor to the code on the bottom, and you have a true first class function.
The code is also a little more concise and makes composition much easier, which we will talk about in a later lesson.
Static Imports
Another tool you can add to your functional belt is static imports. Static imports allow you to import only the static methods of the class in question. This results in a somewhat forced functional approach of using that class. The IDE and compiler will not let you use methods that use instance state. You can then invoke those static functions in your class without qualification.
Why is this better? It guarantees that you are using functions from the importing class that cannot hold instance state, which is just one more barrier to put in front of yourself to achieve a functional design.
One of the downsides to this approach is you can pollute your namespace. As the example above illustrates, we cannot easily tell where GetTotal came from since it looks like just another function. As long as you keep your classes small and follow SOLID this shouldn’t be too much of a problem. Just be cognizant of your namespace pollution if you are statically importing more than a couple classes.
Fun with Functions
Expression-bodied members and local functions are also helpful in functional programming.
These examples may not display the true usefulness of these two language features, but there is still utility in them. Expression bodied members make your functional code more concise, which makes it easier to read. We will go more in depth with expression bodied members in the code example.
Local functions are also helpful if there is a utility operation you must do to get a value, but you will only use that function in one place.
To be honest, I have yet to see great utility in this feature in regards to functional programming.
Practical Applications
Here are some practical tips to make use of what you just learned in your own code.
When you are refactoring, you should look out for chunks of code that can be isolated and converted to pure functions.
This may be functions that can be split into computation and persistence. Functions that can be split into computation and persistence are prime examples. The computation can be converted to a higher order function and the persistence can remain imperative.
When writing new code try to start every new function as a static function. When you reach the point where you must remove the static for some reason, ask yourself why you must remove it. Try to think backwards up the potential call stack for the function to find the issue.
If you can move the imperative requirement for that function, further up the call stack, you can maintain functional purity.
Refactor Challenge
There is a zip file that accompanies this video that contains a program and a prompt for you to apply what you have learned. I have found that doing right after learning helps cement the new knowledge in my mind. So before you continue to the next lesson, consider taking 10 minutes to try the refactor challenge. Most lessons in this course will end with a refractor challenge.
Once you’ve spent some time on the refractor challenge, there is a solution available at DeepTechLearning.com so you can check your work. Remember that there is usually more than one way to solve a problem. If you arrived at a different solution then the one provided, that does not necessarily mean that you are wrong.
Immutability
This video goes in depth into the concept of Immutability and immutable types with a follow along coding exercise to help solidify the concept in your mind.
What is Immutability, why is it important
Immutability is another cornerstone of functional programming. Immutability is the idea that state (or the data of your currently running application) should never change in-place. Once an object is created or a variable initialized, it is never modified.
This practice eliminates many of the complex and nasty issues that are caused by mutating state.
If you’ve ever used or learned about ReactJS, this is a principal rule in how React manages state. One of the difficulties with writing concurrent applications is that accessing mutable state concurrently is unsafe.
Concurrent updates can change the function of an application in an unpredictable way because the data underlying your operation is changing by some other process.
This leads to race conditions, which are notoriously difficult to debug.
Another issue with mutable state is unintended coupling. By allowing state to mutate, anything that modifies or reads that state is now coupled to everything else that reads or modifies that state. Making it difficult to reason about the application on a higher level. Have you ever had to make a change to a function and needed to hunt down all the usages of the parameters in said function and confirm that your change will not cause unforeseen side effects? That exact situation, among others, is easier to avoid while employing the principles of functional programming.
On screen is an example of an immutable class. All the properties on the class have only getters and the values must be set on initialization, enforced by the constructor. You’ll notice that the parameters of the constructor do not follow C# convention.
That is intentional in this case because it allows for similar syntax and usage to the object initializer syntax, which will be discussed in more detail in the follow along. It is important to point out that this example is not completely immutable because the Items list is not immutable. We will discuss this issue later in this lesson when we discuss libraries.
Libraries and C# 9
Now that you have seen how to use the concepts of immutability with vanilla C#, lets go though some libraries that will help make things easier and talk about Record Types that are coming on C# 9.
Ordered from most compatible to least we have:
Immutable.NET which is a great library that is compatible with .NETStandard 1.3 which gives you support for .NET Core from version 1 and .NET Framework from version 4.6.
Then we have System.Immutable.Collections, which is part of the .NET runtime from .NET 5.0 and .NET Core 1.0. Also it only contains features that are useful for handling collections and does not help with objects.
Finally, we have Record Types, Init Only Setters, and With Expressions that are coming in C# 9.0. These features make it significantly easier to use and enforce immutability in C# 9.0.
Links to further resources for all of these are in the lesson notes:
https://docs.microsoft.com/en-us/dotnet/api/system.collections.immutable?view=net-5.0#remarks
https://github.com/mattnischan/Immutable.Net
Referential Transparency
Since you have learned functional purity and immutability we can define referential transparency and look at some examples. Referential transparency is an important concept to understand because it helps you write functional code and recognize when code is not functional.
Referential transparency essentially means that you can replace a function call with its return value and the applications functionality will not change. As you can deduce, this requires that the function be pure and that it does not modify its inputs.
On screen you can see two examples of the same function. One is RF and one is not. Can you see which? I’ll give you a hint, one of them produces a side effect.
Practical Applications
Here are some practical tips to make use of what you just learned in your own code.
When you are refactoring existing code, look for variables in classes and functions that are being modified or mutated and try to convert them to be immutable. Prime candidates are any object with Collections as properties, or object property modifications. Keep in mind the previous concept we learned and try to convert any functions you encounter to pure function while you are investigating immutability.
When looking at functions, pay close attention to their internal state. Temporary variables created during execution are prime targets for immutability.
When writing new code, whenever you are about to assign to a variable ask yourself if this is a mutation. Are you modifying current state? When writing a new function keep track of how you are using variable to maintain internal state.
Also LINQ functions are easy candidates for breaking immutability. To clarify, I am not referring to LINQ when used with Entity Framework as that is generally a different paradigm of usage.
Refactor Challenge
There is a zip file that accompanies this video that contains a program and a prompt for you to apply what you have learned. I have found that doing right after learning helps cement the new knowledge in your mind. So before you continue to the next lesson, take 10 minutes to try the refactor challenge.
There is a solution available at DeepTechLearning.com so you can check your work. Remember that there is usually more than one way to solve a problem in programming. If you arrived at a different solution then the one provided, that does not mean that you are wrong.
Using Lambda Expressions Functionally
This video goes in depth into lambda expressions in C# using LINQ with a follow along coding exercise to help solidify the concept in your mind.
What are Lambda Expressions?
Simply, lambda expressions are just a way to define a function that will be executed at a later time. They can be assigned to variables or passed into other functions as parameters. While they are not the only way to create and use higher order functions, they are a very simple, concise, and powerful way to do so.
If you are familiar with entity framework then you have used lambda expressions whenever you queried a database.
Lambdas under the hood make use of delegates and function very similarly to them. There are some differences in how they operate, but those are not relevant to this course. If you look behind the lambda you will see a func or action type. An action type if the lambda does not return a value and a func if it does.
What are Delegates?
So, what are delegates? They are just references to functions that can be passed around your program as variables.
They need not have a body defined at compile time. The func and action classes are syntactic sugar for delegates, meaning they use delegates under the hood. Delegates, and by extension func and action and lambdas, can be multi casted.
That just means that you can create a list of delegates and one call to that list can trigger multiple delegates in the list.
Delegates also allow for contra and covariance, which makes them incredibly flexible and useful. If you don’t know what contra and covariance are, it’s outside the scope of this course but it is still useful to know.
What are Lambda Expressions?
Building on the previous slides about what Lamas are, they come in two flavors: expression and statement lambdas. While both are useful and necessary, it is important to note that restricting yourself to just using expression Lambdas makes writing functional code a lot easier.
This is not always possible in practice, but if you strive to achieve the strict use of expression Lambdas then you will limit the possible surface area where Lambdas can exist that break our rules of functional purity and immutability.
Here you can see the difference between the two types of lambda expressions. Expression on the left and statement on the right. As you can tell the expression Lambda is obviously immutable. The statement lambda on the other hand, breaks our rule of immutability by modifying the order in place. This possibility of modifications is why it is best if you can change your thinking to produce expression lambdas over statement lambdas.
How to use Lambda Expressions Functionally
Making functional use of lambdas requires a different way of thinking - just like most of functional programming. It is not intuitive to programmers who learned object orientated programming or imperative programming. This is because in many cases using lambdas in a functional way produces code that lends itself well to composition.
This might sound confusing at first but as you will see on the next screen, you have to think backwards when composing many lambdas together. You can perform simple or complex operations in lambda expressions. You are not limited to simple mathematics or simple data transformations.
Mostly, any instruction you can perform in a loop or imperatively, you can perform in a lambda - by extension, most instructions you can perform in a loop or imperatively can be performed in an expression lambda - it just requires changing your thinking a little bit.
Writing your code this way opens the door for composition, method chaining or fluent interfaces, and currying. All of which will be discussed later in the course. These are not strictly functional aspects of C#, but help facilitate the use of functional paradigms and patterns.
The example on screen demonstrates the power of lambdas but there is a couple of things I want to point out:
- Firstly, lambda expressions can be defined as class level functions or locally inside another function.
As you can see on line 7 and line 16.
- Secondly, composing them together requires backwards thinking compared to a normal imperative loop.
As you can see on line 23.
We have to get the applicable items first then apply the tax and then sum them. If you look at line 23, and read it out you’ll see taxrates.sum the applied tax of the applicable items - so it reads out in a backwards order.
We will explore this code in depth in the follow along next.
What is LINQ?
So, what is LINQ? You’ve probably used LINQ at some point of your career, and you might even have a great understanding of what it is. LINQ stands for Language INtergrated Query. If you have used entity framework, then you have used LINQ - it uses fluent syntax - which means it reads like a sentence and action and func predicates to do its work. It is helpful for creating functions and writing concise code.
Libraries and C# 9
Now that we’ve looked at how to practically make use of our new lamda expression skills, here are some libraries and C# 9.0 features that help make things easier.
There are several helpful extension methods in the MoreLINQ library that will help you in writing functional code with lambdas.
The MoreLINQ library is compatible with .NET Framework 4.5 and later, and .Net Standard 1.0 and later.
Coming in C# 9.0 are static Lambda Expressions. This is a way to enforce some type of purity in your lambda expressions. The static modifier disallows the use of local and instance state, which is an insidious problem for Lambdas from a functional perspective.
It is particularly insidious because it is very easy to use local and instance state and also miss the usage of local and instance state in lamda declarations.
Follow Along
Now that we’ve learned about our library and some C# 9.0 features to help us, let’s do another code follow along to implement that knowledge.
Composition and Currying
This video goes in depth into Composition and Currying in C# with follow along coding exercises to help solidify the concept in your mind.
This lesson is going to likely be the most confusing lesson for you. Currying, and by extension partial applications, is hard to grasp at first. If you need more resources about currying and how to use it in C#, there are a couple of links attached to this lesson.
What is Composition?
Composition is simply composing, or combining, functions together ad-hoc in order to create new functionality. It is not the same as creating a function that calls several functions and returns the combined result, though it is similar. The difference is subtle, but important.
Composition allows you to define small, granular functions that you can compose together to create the more complex functionality that you truly desire to create.
You are essentially passing the results from each function to the next function in the call until you reach the end where the final result is returned from the outermost function call.
Taking from our lambda example in the previous lesson, you can see what composition looks like.
We’ll see a clearer example of this later and a clearer explanation of how this is different from imperatively creating a new function that combines the granular functions together. If that definition is really confusing to you, it is explained in more detail in the code follow up.
Without a couple utility functions, using composition in C# requires you to think backwards from your end result. This might sound confusing but it will be made clear in the example in a couple slides. We touched on this briefly in the lambda lesson, and you can see it a little bit in the example on screen.
Imperatively, we would normally get applicable items, then pass those items into sub total, then pass that and the tax rate into apply tax. With composition the way you read it is backwards so you have to reverse it in your mind. You might say it like “We apply the tax to the subtotal of the applicable items.” Which is more concise in speech than the imperative programming equivalent of “We get the applicable items, then subtotal them, then apply the tax.”
What is Currying?
Currying is a complicated concept to grasp at first, but once you do you will see value quickly.
Currying is breaking down a function call of multiple parameters into a function that will accept one parameter and return a function that accepts the remainder of the parameters until you reach the point where all parameters are passed in.
Now that was a really confusing definition, so let me explain it a little differently;
When you curry a function with multiple parameters, you are creating new functions that pass through respective parameters as you use them. The function will then execute with the parameters that you built up over time. This allows you to “call” the function one parameter at a time. This approach is very different from object orientated programming and offers tremendous flexibility.
In object orientated programming, the compiler will yell at you if you don’t pass in all the required parameters at the function call.
With currying, you don’t need all the parameters when you “call” the function. You can build the function one parameter at a time as the parameters come available.
On screen is a contrived example of currying.
Here we are adding multiple discounts to an order. In this case there is no real reason to use currying, but you will see what this allows us to do in the next slide.
The next example will split this out and show you how it would look if you didn’t have all the parameters at the time of the function call.
What is Currying?
Again this is a contrived example, we can see currying at work.
Here you can see that we split the curry call and put some work in between each call - you can easily see how we are building up to the eventual function return for the grand total.
There is obviously an easier way to do this using imperative programming.
The point here is to show you how to do this so you can start to see the value in this approach in later lessons.
One important note with currying is that the parameters in the function chain must all have the same, or correctly mapped, value types. This means, if I tried to pass an integer in as the second discount, it would not work, unless that parameter in the function declaration had an integer in that position as well.
Because of the boilerplate required in C# to get this to work with the compiler, it is a little more obtuse than real functional languages.
If this is all really confusing to you, try not to worry till after the lesson. The code follow-along and the refractor challenge really helps to round out this concept in a clear way. If you are still having trouble understanding this, make use of the resources in the lesson notes.
Currying Continued...
In another example of currying on screen, here we are chaining the ApplyDiscount method 3 times in order to create the required functionality. Again, this is a contrived example and much more easily done with imperative style programming - but here I am just trying to introduce the concept - not show practical uses of it.
Crucially, the methods in the chain do not have to be the same method, the count of input parameters does not have to match, and this can be done infinitely.
The code on screen is not as clear and concise as it could be and that is because C# does not have perfect support for currying. This example, while instructive, is not the best representation of using currying to solve a problem.
Partial Applications
Currying allows you to create and use partial applications on the fly. What this means is that you can take a complex process that is broken up for composition, apply some part of that process to the execution of the program, and save that state in a new function to be used later.
The example code on screen demonstrates this. We create three partial applications in the CompleteOrder function;
One for applying an existing customer discount, and one for each state that we are taxing. These partial applications make use of composition to create novel functionality without writing new functions.
One thing to note in this example is that the discount and taxRate inputs on the ApplyDiscount and ApplyTaxRate functions are swapped versus other examples in this lesson.
This is called a data last function. If you want to make your own life easier with functional programming then strive to write all your functional functions like this - as data last functions.
This means that the actual data that you will perform the operation on is the last parameter. This is the same concept as ordering your function parameters from most generic to most specific in object orientated programming.
If you pay close attention to how the currying is used here you can see that it would not work if we put the subtotal first. We wouldn’t be able to save the function for later use because we would need to know the subtotal beforehand.
Now that was a lot of new complex stuff all packed together, so if you are still a little confused wait for the code follow-along where I explain everything in much more detail, much more slowly.
Currying and Composition together
Currying is extremely useful outside the context of functional programming and composition, but the massive value comes when you combine them. Currying facilitates easier composition because composition requires that output types match the expected input types of the functions being composed.
In the example on the screen on the right, applied tax is expecting a float, in the example on the screen on the right, apply discount is returning a double and ApplyTax is expecting a double.
Back to our explanation;
Functions can take any number of inputs, but only return one value. If you have two functions that take 2 and 5 inputs respectively you must match those signatures, which reduces flexibility and increases the complexity and opacity of your code.
By currying a function with multiple inputs you can fit that function into a composition pipeline with a different number of inputs by currying your function down to the correct number of inputs .
On the left is an example of how currying makes this concise, albeit hard to read, code possible vs. if we didn’t curry on the right.
Now you may be thinking that the right side is cleaner and easier to understand - and coming from an imperative object orientated programming perspective - you’d be right, but there are real benefits to the left side example that are not immediately obvious in this example that we will explore in later lessons.
Libraries
Now, before we get into the code follow-along, I need to introduce a library. I am doing this now because this library makes it extremely easy to use currying in C#. CurryFy provides two types of utility functions. Curry, and UnCurry. It also has some partial application functions that we are not going to explore in this course.
For more details on how this library works see the GitHub page. It is very concise and well explained. We will use this library in the code follow along.
Follow Along
So, let’s jump into the code-follow along.
Practical Applications
Here are some practical tips for how to make use of your new knowledge. When refactoring, look for areas of code that perform multiple operations of computation and split them into more granular functions that do one thing and can be refactored into composition.
The larger a function is the more likely this can be done. Also, code that does data transformations are also a good place to start. When writing new code, it is usually easier at the start of your functional journey to first write your functions imperatively and then convert them to use composition. It is easier to see the functionality in front of you and then decide how you will compose it.
This is harder to do with currying because currying is a fundamentally different way of thinking about your code.
If you find that you are having a hard time using composition, or your code is getting confusing, or has a lot of connective code piecing things together that seems unnecessary.
Refactor Challenge
There is a zip file that accompanies this video that contains a program and a prompt for you to apply what you have learned. Before you continue to the next lesson, consider taking 10 minutes to try the refactor challenge.
There is a solution available at DeepTechLearning.com so you can check your work. Remember that there is usually more than one way to solve a problem so if you arrive at a different solution then the one provided, that does not mean that you are wrong.
Recursion
This lesson goes in-depth into Recursion in C# with follow-along coding exercises to help solidify the concept in your mind.
Recursion is usually a hard concept for developers to fully grasp. There are many good explanations online that teach you how to understand and use recursion in your programming, so in this lesson will instead focus solely on the use and application of recursion with functional programming.
What is recursion?
Simply put, recursion is a function that calls itself until some exit condition is met. An example of recursion is on screen.
Cory repeats himself from 0:41 - 0:48
Note that this example is not technically functional programming. If you need help understanding this concept there are a couple of links on screen and in the lesson notes that explain this in more depth.
Recursion is not just for functional programming, but it will be just another tool in your belt for writing functional code in C#.
Example
On-screen is an example of functional code that could be simplified and made more clear with recursion, using the recursive function at the top of the screen. You’ll notice that in the version on the left, while it is functional, we are declaring and storing three different discounts for the order. While this works, it is repetitive and less flexible compared to the code on the right which is still functional but uses recursion to make it clearer and easier to read.
You will notice on the left that the returning customer discount, the new year discount, and the move old stock discount are all declared at the top and then applied one by one later in the function.
In the example on the right, all the discounts are added to a stack and then applied one by one in the applied discounts recursive function.
While the code on the left is not any more condensed than the code on the right, and the code on the right is not any more condensed than the code on the left, the code on the right - using recursion- is a little simpler and more flexible.
Now since this example is contrived, the recursive version is actually a little more opaque than the non-recursive version. Moving the discounts to a stack removes the context of what discount is which is why the comments are there.
The concept is still sound though, and you will see a clearer example of the benefits in the code follow-along.
Follow Along
Now, let’s jump into the code follow-along.
Practical Applications
These practical applications are simple, but far reaching in their potential work involved and side-effects possible. So take this with a grain of salt, keeping in mind reasonable efforts to accomplish them without going overboard.
When refactoring, consider converting any values you can into Options. This potentially is a lot of effort if you take this to the extreme. Starting with values that are referenced only once or only in a couple places is a good place to start. Also, instituting a rule (either self-imposed, or codified in convention) that all new values must be Options is also a possibility.
When writing new code use Options and Eithers for all values unless it is too cumbersome or impossible. Examples would be if you have a data access layer that interacts with Entity Framework or a presentation layer using Razor. These situations require a lot of annoyance to apply a “Use Options for all values” rule, so it might be best to leave these situations outside of your self imposed rule.
Refactor Challenge
This lesson is short, but the use of options or maybes and eithers in functional programming is a very important concept, so you should take the time to study and internalize how this is used in functional programming.
As always, there is a zip file that accompanies this video that contains a program and a prompt for you to apply what you have learned. So before you continue to the next lesson, take 10 minutes to try the refactor challenge. While I hope you do all the refractor challenges in this course, this refractor challenge has the most potential to help you understand options.
There is a solution available at DeepTechLearning.com so you can check your work. Remember that there is usually more than one way to solve a problem in programming. If you arrived at a different solution then the one provided, that does not mean that you are wrong.
That’s it for this lesson, let’s jump into the next one.
Method Chaining
In this lesson, we are going to go in depth into method chaining, pipelining, and extension methods in C#, with follow along coding exercises to help solidify the concepts.
What is Method Chaining?
We’ve been using extension methods and method chaining in all of the code follow alongs so far. Now we are just naming the patterns. Method Chaining is just what the name implies. We are making a chain out of methods calls.
Another term for this is building a Fluent API - the way you do this is with extension methods.
Extension methods are static methods that attach to an existing type to extend it. We can build on top of an existing type without creating a derived type or modifying the existing type.
A good example, as seen on screen, is StringBuilder. With StringBuilder, you extentiate a new StringBuilder type and then use append to string new strings onto the end of your instance. Here you can see that we are “building up” our string using methods that are chained together.
What is Pipelining?
Pipelining is just a fancy term for what method chaining does as a pattern.
It is the passing of the output from one function to the input of another function, so in a fluent API when we are calling one method after another, you are taking the output from the first function, passing it as the input to the next one etc.
Limitations
So, what are some limitations with extension methods?
Well, extension methods must be public and it must be static.
They will also appear in Intellisense on the targeted type, potentially polluting your Intellisense.
You can also not modify existing extension methods on a class.
With those limitations in mind, extension methods are still extremely powerful.
Follow Along
Let’s jump into the code follow-along and we will do some examples of method chaining and extension methods.
Null Handling
In this lesson we are going to go in depth into handling Null values functionally in C# with some follow-along exercises to help you make use of your new knowledge.
How do we handle Nulls functionally?
Handling nulls is always a difficult part of programming in languages that allow them. Because of hard to track bugs, among other reasons, many functional languages do not allow null values at all. C# does, which means we need to use a library to accomplish a similar feature.
Null reference exceptions are sometimes very difficult to track down, and many times the stack trace of such an error leaves a lot to be desired.
One particularly valuable approach to solving this is Option and Either types.
Options, also called Maybes, provide an interface to model data in such a way that a developer is required to explicitly handle the presence or absence of a value. This forces the developer to model the data contract more explicitly, and removes null reference errors completely. Options have two different states: Some, which will contain the value and represents the presence of said value; and None, which represents the opposite absence of a value.
Eithers are similar to Options in that they require the developer to think about the states of the value. They differ in that they are allowed to return an Exception.
The difference between using and Either or Not is that the developer must define what exceptions are allowed to return when the value is instantiated. Forcing the thought of error handling to the foreground.
We will talk more about this in the error handling lesson.
Using an Option/Maybe Type
So how do we use Options? Well the easiest way is to use a library. If you can’t add a dependency to your project then you can write a close approximation pretty quickly using Nullable Types.
The Option library I will be using in the code follow-along and the Refactor Challenge later is called Optional, linked here on screen and in the resources for this lesson. This library contains some nice helper functions to make life, and data transformations with options a lot easier.
One of those extensions is OR. OR will return the value in the Option, or a default value specified. It will return a Some instance if the Option is None. This doesn’t really make a lot of sense now, but once we go through the code follow-along it will become clearer.
Another helper function is else. ELSE will return an alternative Option which will replace the current one if the current one is None. Since this returns another Option, it is possible to return a None or to chain ELSEs together.
MAP will transform the inner value of the Option and return a new Option. If the root Option is None, then None will be returned.
There are other helper functions in the library that the documentation vert explains clearly and concisely, so I won’t go into detail here. These explanations may not make sense to you yet, but I thought I would expose them to you now, before we get into the code follow-along.
Why not use Nullable<T>?
If you are familiar enough with C# you may be asking, why not use the nullable generic type? That is a valid question and nothing is stopping you from doing that. The C# nullable type is very similar to Options. They have the same general purpose, representing nullable or empty values.
There is only one real difference between Options and the built-in Nullable Types, and that is the convenience methods that Option exposes. You could always write your own convenience methods that sit on top of Nullable types if you needed to, but the library is more convenient.
Follow Along
Now, let’s jump into the code follow-along.
Practical Applications
These practical applications are simple, but far reaching in their potential work involved and side-effects possible. So take this with a grain of salt, keeping in mind reasonable efforts to accomplish them without going overboard.
When refactoring, consider converting any values you can into Options. This potentially is a lot of effort if you take this to the extreme. Starting with values that are referenced only once or only in a couple places is a good place to start. Also, instituting a rule (either self-imposed, or codified in convention) that all new values must be Options is also a possibility.
When writing new code use Options and Eithers for all values unless it is too cumbersome or impossible. Examples would be if you have a data access layer that interacts with Entity Framework or a presentation layer using Razor. These situations require a lot of annoyance to apply a “Use Options for all values” rule, so it might be best to leave these situations outside of your self imposed rule.
Refactor Challenge
This lesson is short, but the use of options or maybes and eithers in functional programming is a very important concept, so you should take the time to study and internalize how this is used in functional programming.
As always, there is a zip file that accompanies this video that contains a program and a prompt for you to apply what you have learned. So before you continue to the next lesson, take 10 minutes to try the refactor challenge. While I hope you do all the refractor challenges in this course, this refractor challenge has the most potential to help you understand options.
There is a solution available at DeepTechLearning.com so you can check your work. Remember that there is usually more than one way to solve a problem in programming. If you arrived at a different solution then the one provided, that does not mean that you are wrong.
That’s it for this lesson, let’s jump into the next one.
Error Handling
In this lesson, we are going to go in depth into handling errors in a functional fashion in C# with follow along coding exercises- as always, to help solidify the concepts in your mind.
How do we handle errors functionally?
The normal way of handling errors in C# is cumbersome and makes code harder to read, here are some rules for handling error states and exceptions functionally;
Most of these rules and patterns don’t apply to just functional programming, but will really help you in any project.
For most developers, at least that I have worked with, error handling is an afterthought.
It is not considered a large part of the program and is certainly not thought of as part of the architecture. Usually you just wrap some stuff in try catches, log the error somewhere, and return the user to an error page of some kind. Maybe you’ll try to recover from the error in some way. It becomes burdensome to write error handling or validation code.
It makes code harder to read and bloats the code so that the purpose is less clear.
A lot of developers have probably heard the adage, “Do not use exceptions for flow control”. I know that I am guilty of not following that one. It wasn’t explained, to me at least, what you should do otherwise.
This lesson will show you how to handle errors functionally, which has a side effect of improving how you handle errors in non-functional code. Using try/catch and throw are generally not allowed in functional code as they interrupt the flow of the program, which is technically a side effect. Errors should be handled as low in your code as possible.
For instance, if there is an object with a field that cannot be empty, handling that with it comes from the database, not when it gets to the UI is more appropriate.
This simplifies the higher-level code where you are composing and transforming. Which is usually the more complex code than the low level stuff.
One of the patterns you will learn is using a Result or Either class to define your data objects. This means that your business layer objects will not be built using primitives.
Some people will scoff at that because they were taught that it is best to build your objects as POCO’s, or plain old C# objects using primitives - for things like your data or data transfer layer this is fine but where your business logic is concerned, you will see that getting away from primitive obsession is a good thing.
One more rule, if an operation can fail or has invalid states - those failures and invalid states must be represented in its return value. This is why we use a Result or Either return value, which we will see next.
It goes without saying, but I’ll say it anyway; unexpected exceptions are, well, unexpected so don’t feel like you have to spend hours thinking about all the ways that your code could go wrong in order to capture all those errors in your state representation.
You’re only human after all.
Using Result or Either to Handle Errors
As I mentioned in the last slide, we will explore two options to handle errors functionally. There are many ways to do this, so if you have an idea for a pattern that you like better, go for it.
The intent here is to make it easier to handle exceptions and errors and these are just a couple of ways to do that. We’ll start with a simple result class; you can see the definition on screen.
The code for this will be in the follow-along and we will walk through building it together, it is also on the deep tech learning website with the refactor challenge solutions. This result class is a very simple and quick implementation that checks all of our error handling boxes.
We can even create some extension methods off this that will make pipelining and method chaining a snap. We can also use either, from the language extensions library we learned about earlier.
We will look at an in depth at an example of how to use either next.
The benefit of doing things this way is that the contract for the value you are dealing with is well defined and easy to reason about just by looking at function signatures. This creates referential transparency, which is a tenant of functional programming. It’s ok if this benefit doesn’t make sense or isn’t completely clear to you yet.
We will see it in more detail in the follow-along.
Handling Errors with Either Example
Let's look at an example of using Either to handle errors. By convention, the left side of the either will always contain the error value. Whether it is an exception, a message, or a magic value. In the example on screen you can see how we will integrate the either for ApplyDiscounts. This example is just a simple example of how to set up Either for error handling.
In the follow along we will write a much more complex and powerful example and you will get to see how using either opens a lot of doors in your functional code.
On the right you can see that using the either return type allows you to return either type from the function. In the case of the example on screen an exception, or a double.
Follow Along
Now, let’s jump into the code-follow along.
Practical Applications
When refactoring consider converting the return values of well isolated, or sparsely used classes into Result or Either. This gives you an immediate benefit without requiring tons of risky refactoring. Expanding on that, also consider using Either or Result as return values for all new methods that you write. If you enforce this rule, then your code can only get more and more functional with time.
When writing new code never return primitives from methods in your business logic layer unless you know that the method has no expected failure conditions.
Refactor Challenge
There is a zip file that accompanies this video that contains a program and a prompt for you to apply what you have learned. I have found that doing right after learning helps cement the new knowledge in your mind. So before you continue to the next lesson, take 10 minutes to try the refactor challenge.
There is a solution available at DeepTechLearning.com so you can check your work. Remember that there is usually more than one way to solve a problem in programming. If you arrived at a different solution then the one provided, that does not mean that you are wrong.
Yield Return
In this lesson, we are going to explore yield return in C# with follow along coding exercises to help solidify the concept in your mind. This is going to be a pretty short lesson, so let’s jump in.
What is Yield Return?
Well, Yield Return is a less popular C# feature that you may not have heard of.
It is a signal to the compiler that the code block in question is an iterator.
It creates concise and compact code, as you can see in the examples on screen.
You can return values with Yield Return without specifying a type in the function body of the return value.
Now, not just for functional programming - you can use this in any of your code.
In the sample on screen, you can see the yield return being used in the path example. The GetApplicableTax function is on three lines, its sibling on the bottom of the screen is 5 lines because we have to declare the type and list the tax rates and return that at the end of the function, so you can see how Yield Return makes codes more concise, compact and easier to read
Follow Along
Let’s jump into this quick code follow-along and we will convert some stuff to use Yield Return.
Take your code to the next level of maintainability, efficiency/concurrency, and ease-of-understanding. Learn the principles of functional programming, how and where to apply it in C#, and why it will make you a better programmer. Improve your software’s architecture and leapfrog your code quality over that of your peers.
What You’ll Learn
Why functional programming is useful
How to apply functional principles and patterns in C#
Design and Implement Immutable Types and Maybes/Options (Null values)
Writing Pure Functions (functions without side effects)
Using recursion with functional programming paradigms to simplify your code
Using LINQ to clean up code and condense logic
Handle errors in functional style
Utilizing extension methods to write fluent, easy to read code.
Make use of new C# 8 and 9 features in functional style
Course Description
Learn how to apply function paradigms and patterns to codebases in C#. If you want to improve the maintainability, understandability, stability, and terseness of your code; this course will help you do that. This is meant for experienced C# developers that already have a solid, moderately deep understanding of the C# language.
Each video will introduce a concept, and lay out an example of implementing that concept.
If you want to improve your code’s quality, make your application faster and easier to understand, and increase your own productivity, then this course will help you tremendously.
Each video in this series can be watched independently. You can get value out of each video by itself if you already know certain concepts or are looking to learn something specific.
Teaching Style
All videos are short, to-the-point, and avoid fluff.
The course covers the following topics:
Theoretical Foundations of Functional Programming
Core Concepts of Functional Programming
Functional Features in C# (C# 9 included)
Immutability, Extensibility, and Extendibility in C#
Dealing with Errors and Nulls gracefully
Course Keywords:
C# Clean Code
C# Best Practices
Functional Programming in C#
C# Functional Programming
Clean Code in C#
Functional Programming
Code Readability
Stable Code in C#