I read Enrico Buonanno’s Functional Programming in C# directly after Jon Skeet’s C# in depth. Unlike Skeet’s book, this is not a book about C#, it is a book about functional programming which uses C# is the medium of instruction.
What makes this particularly interesting is that C# isn’t really a functional language. Usually functional programming is discussed in terms of niche explicitly functional languages like Haskell. By using C#, Buonanno makes the principles and practices of functional programming a lot more accessible to the average programmer. He does advocate mixing the more traditional object oriented style of C# with functional programming, but this is not something that really comes through in the code samples.
This book covers the basic concepts of functional programming really well. He explains how and why to avoid state mutation, the concept of functions as first class citizens and higher order functions, function purity and side effects, Partial application and currying and lazy computation. One thing that interested me particularly is the good case the author makes that pure functions should not throw exceptions.
He does not spend a long time justifying functional programming. The two main benefits he repeatedly highlights are easier testing and better support for concurrency. It is clear that a functional approach is more suited to concurrent programming. However the claim that functional code is easier to test seems somewhat dubious to me, and is not really backed up with credible examples. He also claims that functional programming leads to cleaner code. Again, I am somewhat skeptical.
I really enjoyed the discussion of user defined types. In particular the pattern of creating new types that wrap low level types and add some semantic meaning. A great example he uses is an age type. This has only one member, an integer. It’s constructor will only accept valid values for human ages. This means that we have added an extra layer of static type checking to this type: when we want an age, we use the age class, and not just an integer. It also means we don’t have to perform extra checks when using an age type, as we know it’s value was checked when it was initialised.
The material on LINQ was also quite good. LINQ is strongly functional in style, it emphasizes data flow over mutation, composability of functions and pureness. This is all well explained, the author even shows how to integrate user defined monads into LINQ. However LINQ is not dealt with in a single place in a systematic way, which is disappointing. Another gripe I have is that the author uses LINQ query syntax. I do not like query syntax. Not only is it ugly, it is usually incomprehensible.
Buonanno uses Map, Bind and Apply to introduce functors, monads and applicatives in a very practical way. He also goes into detail explaining how and why to use the classic monads Option and Either. We even see variations on the Either monad that can be used for error handling and validation. Both the applicative and monadic versions of Traverse appear near the end of the book as well. In my opinion, Monad stacking is one of the worst anti-patterns of functional programming. So it is disappointing that it only gets very limited coverage. I would also have liked if he had used his excellent examples as a jumping off point to dig deeper into category theory. Perhaps however, that would have been too abstract for what is quite a practical book.
Handling state in a functional way is covered well, but it feels a bit academic. It is hard to imagine applying the patterns he covers in a real world code base. Sometimes you just have to use state! The final few chapters cover IObservables, the agent model and the actor model. These sections were quite interesting but felt a little out of place. They really merit a much deeper dive.
When I finished this book I had a much greater appreciation for functional programming. In particular it gave me lots of ideas of how I could practically apply it in my real life work. The code samples were all very good, occasionally though they were a little convoluted. Indeed they sometimes seemed like evidence against a functional style. But, as someone relatively new to functional programming I appreciated how grounded it was in real world examples. Overall, this book is an excellent resource for C# programmers who want to add a little functional flourish to their code. I highly recommend it.