On this episode of Eat Sleep Code, guest Dave Fancher talk about Functional Programming. We discuss how functional programming has made its way back into modern development, clean coding, and more.
Dave Fancher is the owner of Achiiv Solutions, LLC in Carmel, Indiana, a three-time Microsoft MVP, author of The Book of F# from No Starch Press, Pluralsight author, and InfoQ contributor. He has been building software for more than a decade with an emphasis on Microsoft technologies. Dave is active within the software development community and has spoken at numerous events throughout the United States. When not writing code or writing about code he enjoys spending time with his family, watching movies, and gaming on his Xbox One.
Ed Charbeneau** :** Hello and welcome to Eat Sleep Code, the official Telerik podcast. I'm your host, Ed Charbeneau, and with me today is Dave Fancher.
Dave Fancher** :** Hello!
EC: And today Dave and I will be talking about functional programming in .NET. Dave is author of, The book of F#, from No Starch Press and a Microsoft MVP for .NET and also the owner of Achiiv Solutions. Dave is a Pluralsight author, and he's done a course called, "Building F# Type Providers" and he's working on a new course about functional programming with C#. So Dave what is functional programming?
DF: So ask ten different programmers what functional programming is, and you'll likely get ten different answers. But in general, they tend to boil down to three main characteristics. The first one is that functional programming is about controlling side effects, side effects are anything that changes the external state of the system. It could be as benign as writing to a log file, or it could be more nefarious like changing some shared data across the system. Under functional programming, we wanna control that as much as possible, also under functional programming, we tend to focus on expressions rather than statements, so C# tends to be statement-based. There are some expressions in there, but for the most part it's statement based, your if statements, your using statements, and so on, versus a functional language tends to be focused on expressions, rather than executing something for its effect we wanna execute something for its result. And then finally, functional programming is really centered on the idea of mathematical functions. And so functional languages want to treat functions as data. So you can pass around functions just as you would an integer or a string and that gives way to higher order functions, which is where most of the power of functional programming comes from.
EC: So you're controlling the mutability of the data in the application.
DF: Largely yes. So a language like F# is going to make everything immutable by default. C# has capabilities for making things immutable, especially in C# 6, where we now have the getter-only auto properties, which makes that a lot easier. Getter-only auto properties make it much easier to create immutable data types because behind the scenes it's doing that wiring up of a read-only backing variable, which we would have to do manually prior to C# 6. And by saying that you can't change something, you don't have to worry as much about some other thread coming in and changing data out from under you or any of those other types of effects that cause people to say, "Avoid global state [laughter]"
EC: So every time I talk to somebody that considers themselves to be a functional programmer, they're using languages like LISP and Erlang, and they are heavy advocates of those things, they tend to really be enthusiastic about functional programming once they discover it. And I know you are dealing a lot with F#, so what makes F# that language of choice for you? What gets you so excited about functional programming with F#?
DF: What drives people to functional programming is that... When you start working with the languages, whether it's Erlang, or LISP, or F#, the way that functional programs are written leads to greater predictability and a better capability of being able to reason about your code. It's the whole deterministic thing, if you're not changing values from... If you're not changing global state, you don't need to keep track of it, you don't have to guess, "If I execute a particular function or method, what effect is this is going to have on the overall state of the system?" Because you're guaranteeing that you're going to get the same result every time.
The other thing is that you end up with less complexity. So think about a multi-threaded scenario. Again, coming back to immutability and controlling side effects. If your code can't change the system state and you have a collection, you don't have to worry about another thread changing that value from under you, you don't have to worry about locking, you don't have to throw in monitors, you don't have to throw in any of these other primitives that they've taught you to worry about synchronizing threads because there's no data to synchronize, it's always going to be the same. You see this a lot in LINQ, going out of the functional languages specifically, LINQ is a functional domain-specific language inside of .NET. And if you have a collection, the LINQ extension methods are not going to change that underlying data. So you're guaranteed to get a new collection from executing a LINQ to objects expression, for instance.
EC: Yeah. One thing I've noticed with functional programming, in my experience, is it leads to more deliberate sets of functions, and it leads to more human-readable code by having these smaller, more precise functions. You know what each function is going to do, you can name those things appropriately. And then you have this chain of API calls that's almost in a human-readable form. So I see a lot of fluent APIs popping up out of the functional side of things. Is that something that you've seen in your experience as well?
06:04 DF: Yep. Actually, so most of my day job is spent with C#, but I treat C# as a functional language now. One of the talks I give is actually, "Functional C#." And in that talk, it's all about building up a couple of extension methods that bring fluent APIs and method chaining, basically, functional pipelining, into C#. And my C# programs use a lot of that now, and what ends up happening, especially under C# 6.0, is I end up with these functions that are really just a chain of function calls, a functional pipeline, and it ultimately results in a single expression, which means I can make a lot of use of expression-bodied members.
EC: Now, some of these changes came up, like you said, in C# 6, but I think a majority of it came up in C# 3.0, did it not?
DF: It was in 3.0 or 3.5. Whenever LINQ came out. 'Cause when you look at what LINQ it, it's really a collection of different technologies that you can use independently. So you've got of course Lambda Expressions making delegation finally usable. You have anonymous types, you have generics, which came out in 2.0. So all of these things working in near perfect harmony to allow you to put together expressive expressions, so to speak, allowing you to chain things together. And the thing is... And I think back to when I first started with LINQ. LINQ has been called the gateway to functional, and it really is, and I think back to when I first started getting into it, and learning about LINQ, and seeing all the things that it was doing, and it just clicked with me. It meshed with the way I thought, and understanding that this is really just a bunch of higher-order functions, that I didn't know they were called higher-order functions at the time when I was getting into it. But as I've gotten more into functional and understanding what this is doing, it's led me to understand that imperative code like we typically see in C# tends to obscure a lot of patterns that functional programmers have recognized for decades. For instance, we end up writing a lot of for loops and then nested conditionals to filter or sort a data list.
In the functional world, if we look at just staying in F# for now, since we're talking about .NET, there's a series of modules for doing just that. Filters, you have sorting, all of those built in, and now C# has some of that, but it's all built specifically on the objects, like array has sorting built in, but it's always static methods on that type, as opposed to being modules and working off of more generalized types like IEnumerable <T> for instance.
EC: Yeah. I've noticed that when LINQ came out, it was something that for some reason, I gravitated to very quickly. And when I go to user groups and conferences and things, I talk to people, and it's still not as popular as you might expect it to be. And I feel like if people got more into it, and looked at how simple some of it really is... It is a mystery at first. It's not your normal style of programming, like you said, with iterations through loops and using ForEach and things like that. Now you're using these different filters and iterators through LINQ. But if you learn about it a little bit, and then see how easy it is to extend those things using extension methods, you could come up with some really clean and powerful code with just a few, simple tweaks.
DF: Absolutely. And the thing, going back to my point, functional programmers have been using these patterns for years, but when you're working in an imperative style, you get so focused on, "Well, I need to iterate over this sequence." And so you start focusing on those plumbing details. And for the most part, unless you're in some very specialized case, you really shouldn't care how it iterates. I don't care how the LINQ where method iterates over my collection. I don't care whether internally it's using a ForEach or a while of any of that stuff. I just wanna get a filtered list back.
Now if you have a good reason for going to the imperative, it's probably gonna be a little bit more performing, but in your general line of business apps, working with typical data sources that at least that I've seen in my career, forcing yourself into doing the for loops and that is a micro-optimization that doesn't need to be done. Unless you can say, "I'm having this performance problem because this Where Extension method is iterating differently than I want it to, and it's doing something." Even at that point, what are you doing inside of the lambda expression you're passing into it? Are you doing some logging, are you doing something else? It's like you're talking about nanoseconds. And if you're doing some machine learning off of
NOTE: 'That's at 11:35 and I couldn't get the word either – it's probably not what the transcriber wrote, though.']
multiple millions, yeah, you probably don't wanna use LINQ, but you're probably also not using .NET anyway. [chuckle]
EC: Yeah. I think a lot of the people that I've talked to come from a different side of it, where they're just not familiar with the LINQ operators even being there. They're more... Or they may know about them but they're more familiar with... This is the way I've programmed for 10, 15, 20 years using for in each, that repetitive looping through things.
DF: Yeah, and I think before LINQ... Going back before LINQ, you had to do it that way basically. There were some places in the .NET framework that would allow you to work somewhat functionally, but if you think back to the days before, even before LINQ came out, then you had some facilities for delegation, but they were almost unusable. The very early days of .NET, the only thing people used delegation for was event handling, right? So to me, the reason that people didn't use delegation, one, was a lack of understanding, but you have to do a lot of plumbing code to get it to work. And then, eventually, anonymous functions came along and they improved the story behind using delegation a little bit, but you still had to have inline delegate keyword and it was basically a full method declaration inline.
And then finally, when LINQ came around and brought lambda expressions with it, it became usable. You could put your variables or your parameters inline. And you had an arrow with an implicit return in most cases and it became usable for the first time. And now with lambda expressions you start to see it permeating different parts of the framework. Look at the Task Parallel Library, for instance. Everything you want to do with the Task Parallel Library is based off of delegation. You have your Task Factory, and it accepts an action or a func depending on what you want that task to do.
EC: And I'm seeing more and more features, like you said, with C# 6, and then some of the frameworks that are being released in the future here such as MVC 6. There's even more functional-style programming and function delegates and things of that nature being introduced everywhere into that framework, especially with configuration and dependency injection and all of those things.
DF: Yeah, and I'm excited about where C# is going, I just worry that... It's a very good object-oriented language and I just hope that it doesn't get too polluted on both sides, that it ends up with too many different ways to do the same thing. I like the functional features, but C# I think, really, its strong suit is object-orientation. It doesn't have a lot of those default things that a language like F# does. Things like the default immutability. The type inference system in F# for instance, is much stronger than it is in C# and so, because of the way that... Just because of the nature of functional programming, a language like F# is more suited to it because it's written with that paradigm in mind. F# is called a functional first language for a reason. You can do object-oriented code in it, but you should really try going the functional route first, it'll result in a better application.
Conversely, with C#, C# is an object-oriented first language and it really shows that. So if you wanted to do something, a technique in functional programming with pipelining, uses partial application of a function so if you wanna do that in C# it's perfectly doable but you end up with method signatures, with angle brackets everywhere because you're having to tell the compilers so many times what type it is because the language in the compiler isn't set up to be able to infer those as easily as the F# compiler is.
EC: So, we've talked quite a bit about functional programming in C#, with LINQ and function delegates, lambda expressions. What does F# have that C# is lacking? What are some of the things that don't transition across? For example, is there any way to do pattern matching in C#?
DF: I think Tomas Petricek did a satirical post about how you can use exception types to do pattern matching in C# [chuckle] But really, the closest thing that we have to pattern matching in C# is a switch. And as we all know, that's pretty limited to things that the compiler can see. So things that F# brings that C# doesn't have. First off, I like the stronger type inference. You can, in many cases, get away without having any type annotations on your binding, so the compiler can just figure it out. I've always been told that the computers are good at doing the same thing over and over. Well then, why do I have to keep telling the C# compiler over and over what data type I'm using? [chuckle] If a compiler can figure that out I'm perfectly happy to let it do it's job.
The next thing we touched on a little bit is pattern matching. And there are tons and tons and tons of different ways you can take a value and use pattern matching to isolate it, to branch your code according to the values in your data type. And it can go from anything, from integers and strings, all the way into more complex types. You can use tuples. You can use the record types. You can use discriminated unions, which brings us to the next point, is that in F# you get a whole series of data types that just aren't available in C#. I've mentioned two of them already, the discriminated unions, which basically say that your data can only be one of a series of values. So you're restricting it down to just the union cases that are defined in that discriminated union. And then the other type is the record types which are basically, you can think of them as light weight classes, they compile down to classes but it's a single line usually, or a very small, concise definition for holding some data. And then if you wanna go even more general than that, tuples are first class citizens in F#, so you don't have to deal with the tuple.create, and item one, and item two. So tuples are actually usable in F#.
EC: So let's back up for one second 'cause we're throwing a lot of words around that people [chuckle] may not be familiar with. Can you explain pattern matching just a little bit for folks that may not have experience with it?
DF: Sure. So pattern matching is like a switch statement on steroids, it's how it's usually described. So essentially you're saying you have a data type and you wanna branch your code according to certain conditions on that. So you start listing out... You can think of a switch statement you have, you wanna match against an integer, you do case one, case two, case three. Well, pattern matching takes that to the next level, and it says, "I have a record type and it finds a person that has a first name and a last name, and I want to execute one branch of code if a person has a particular last name." And you can also then capture those values and pass those into the particular case body and use those. Essentially, you're putting another function inside of the pattern matching. So you can do a lot more with the pattern matching just based on its nature, being able to work with things that aren't necessarily visible to the compiler. Like, the compiler doesn't know what a value is. You can work against things that aren't constants, for instance. You can look at and compare it against another value. You wanna say, "I've got an integer and it needs to be higher than something." You could do it as an if-statement in C#, but pattern matching tends to make it a little bit more concise.
EC: So if we wanted to get started with functional programming in.NET. Let's start with F# first and then talk a little bit about C# afterwards. What kind of resources could we go out and grab for learning F#?
DF: So I always tell people to start with my book, [chuckle] but beyond that there's a really great website, it's F# for fun and profit, and that covers... It tends to be focused on F#, as its name implies, but Scott, the guy who runs that site, does tend to get a lot more into the theoretical, functional programming side of things. It's a really great website. Of course F#.org has all sorts of great resources and testimonials, anything from the F# Software Foundation is a good place to start on those things. As far as functional and C#, just start with LINQ, learn the principles, how does LINQ work? And that will give you a good grasp of how you can start getting into that functional programming in C#.
EC: So let's go back and talk about pipeline style programming again real quick. So let's go a little bit deeper into fluent APIs and how those work with all of this.
DF: In functional programming, and I'll speak specifically about F# since that's where most of my functional experience comes from. We have a series of operators called the pipelining operators, and then there's a pair of related operators built in for function composition. And they have a similar effect, but basically the idea is to let some data flow through the system. Now whether that's for transforming it into some other value, or executing some side effect against it, we can do that through pipelining. And basically we take a value and then use one of the piping operators to apply another function. That value goes into the next function as the final value through partial application, and then we do whatever needs to be done to that. And that allows us to easily compose our functions, and as an extension, the whole application.
Fluent interfaces tend to be the object-oriented version of that. If you look at LINQ, for instance, you start off with a sequence, and you wanna filter it. You do.where. Well, the where extension method is a higher order function that takes an expression for how to filter out that data. So it operates against that source expression. If you look at the signature for an extension method, what do you have? You have the first parameter decorated with this. So basically it's the object oriented equivalent. And by doing that... You see it in jQuery as well. jQuery is really built around that whole notion of function chaining. So you do your selector.whatever.whatever. You do the same thing in LINQ. You do sequence.filter, sequence.where, sequence.select, and you chain all these things together to allow your data to flow through the system. And then by adding some additional extension methods to the application, we can allow that to carry throughout the application.
DF: About a year ago, Mark Seemann put together an article. It's on his blog, ploeh.dk. It's called ' SOLID: The next step is Functional' This is a reference to the SOLID principles that object-orientated programmers love, and when you really dig down deep and you look at them, boil them down to their essence, what you really see is that they're guiding us towards functional programming, single responsibility. Well, what's more single responsibility than a function that does just one thing. And so, when you really dig into it, I recommend going and reading that article, I've reached similar conclusions.
DF: The other thing that I came across is there's a book that everybody loves. It's on many people's must-read programming books, and that's Bob Martin's 'Clean Code.' I actually have it sitting right next to me right now. A lot of that book focuses on things that are a little bit more abstract, things like naming and things like that, that languages can't necessarily control but, there's an entire chapter dedicated to how you write functions. And what you find when you look at those rules is a lot of them, again, are guiding towards programming in a functional style.
DF: Now, the book is written against Java, of course, but it applies equally well in C#. And when you start looking at the essence of them, again, just like SOLID, it's guiding you towards programming in a functional style. And why not, if you're gonna be guided towards working in a functional style, use the tools? Rather than building complexity to enforce those and relying on discipline, let's start using tools that actually do it for us. And functional languages and things like higher order functions, even in C#, will allow us to more naturally flow into that and then, we just start following the guidelines laid out in Clean Code anyway.
EC: Yeah. So, I really appreciate you coming on the show, Dave. I think by talking about these different styles of programming, different languages, challenging the way people think about the way they're doing development, hopefully gets people in a mindset where they're gonna go out and grab some of this information and learn some new things. Whether they like functional programming or not when they're done, at least, that they come back with a new perspective on something they already use, will be a big help to everybody. So, thanks for coming out and doing this with me.
DF: Hey, no problem. Thanks for having me on. I appreciate it.
EC: If you'd like to catch up with Dave, you can find him on Twitter @davefancher. His book is out there, The Book of F#, and you can also catch him on Pluralsight. He's got a new course about functional programming in C# coming up.