I have been asked a few times "how I got started with F#?" as more than a few people have found it difficult. I myself had a few false starts with it. It looked weird, I didn't know where or how to start, it was too different to OO with C style languages, and the tooling just was not as slick. I honestly think a better question is "Why did I start using F#?"
The WHY of it
As I have matured as a developer I have come to appreciate coding practices that constrain my options in a way that minimizes potential errors. An infinitely flexible design is also one the allows all possible errors, known and unknown. Constraining future developers to "make illegal states unrepresentable" cannot be overstated as a design goal. I sometimes say "code like the future developer on this is an idiot because current you is an idiot and future you will be to". To be clear, I say that to myself, about myself.
In OO we do this with constructors or factories (and hidden constructors), with encapsulation and smart APIs. This is a big part of the guidelines around aggregates in Domain-Driven design (DDD) and keeping the aggregate consistent. We have a lot of patterns and practices in OO that help with this. A LOT! In fact it is quite difficult for new developers to get up to speed with them all. And since they are often struggling with the technical implementation of features they are not worrying too much about the intricacies of the design and whether it leads future developers into the pit of success. We coach, and hopefully with good coaching they learn these things faster than we did through trial and error. I cannot help but wonder if there is a simpler way to get to well designed software than absorbing all these patterns and practices? Note I said simple, not easy.
Functional programming (FP) with its mathematical basis makes some claims about correctness. Correctness is hard to be certain of when global state is constantly in flux, as it is in an OO centric application. FP revolves around functions, with inputs and outputs, with the same input always yielding the same output (for pure functions).
So basically the WHY can be broken down into 2 points:
- Correctness of the program
- Fewer concepts need to be known to develop maintainable software
I remember reading this article of Mark Seemann's and thinking this seems like a problem I have but I cannot quite relate to his conclusion. As we will see in the next section, it took me 2 years to get to a place where I could read that article and nod my head instead of scratch it.
The HOW of it
I was not keeping notes so these are the highlights I remember and that I think are important.
Since about 2013 I had been trying to learn and apply many of the technical approaches highlighted in DDD. This lead to much more focus on types whose instance state can only be changed in a very controlled way. Not only that but the types are descriptive of the domain and do not try be too reuseable but rather represent very specific use cases.
By the time 2016 rolled around I had heard of the promises FP made and had even "file new project"'ed an F# console application but with very little success. I resolved to give it a better try and started reading through fsharpforfunandprofit and looking at a few Pluralsight videos.
Then I was contacted by Manning to give feedback on an early draft of Functional Programming in C#. In it Enrico Buonanno gives a really deep introduction to functional concepts and patterns, showing both the implementation and usage of FP in C#. For me this was quite nice as I could absorb concepts without getting hung up on the syntax of some new programming language. These inspired a series of posts on Honest Types, namely Honest Arguments, Honest Return Types, and Better Error Handling.
By early 2017 I was writing small console apps in F# that would crunch some CSV files, or merge some PDF documents. These were not great and I realized that although I was getting used to F# syntax I was missing something key in how to structure my applications. The penny only dropped when watching a video from Mark Seemann on Functional architecture - The pits of success. Another good one released later is From Dependency injection to dependency rejection. Both of these talk about purity and composing applications so the code with dependencies on IO are on the outside. If this sounds like Clean/Onion/Hexagonal Architecture, you are absolutely right.
Now here we are at the end of 2017 and and I have just finished Domain Modelling Made Functional by Scott Wlaschin of fsharpforfunandprofit fame. It brings together so many deep topics in such an approachable way that it is difficult to compare to any book I have read before. It doesn't assume any knowledge and yet I learned some F#, some FP, and some DDD even though I have read multiple books dedicated to each of these topics. Scott develops a feature from beginning to end in a practical way that distills and teaches the core concepts of these advanced topics without getting bogged down in theory. I realize I am sounding like a fan boy here but I would honestly recommend this book to teach FP and F# OR DDD. It teaches both brilliantly.
This December I posted my first F# themed blog post as part of the FsAdvent Calendar 2017. I submitted my first PR to an F# open source project and now I am winding down on my 2nd FP related blog post. I am looking forward to what the next year brings and all I have to learn.
Further Reading (posts)
- Mark Seemann has a brilliant posts on how a language can reduce the potential for errors
- Scott Wlaschin on learning F#
Further watching (videos)
- Mark has an excellent talk on falling into the pit of success and another on Dependency Rejection
- Designing with Capabilities
- Railway oriented programming
- Domain Modelling Made Functional
- Functional Programming in C#
- Real-World Functional Programming
- Header photo by John Mark Arnold